Load, combine, and filter variant data
Load clinvar data
clinvar_hg38_EGFR <-
read_tsv(clinvar_EGFR_path)
Rows: 2444 Columns: 34── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (23): Type, Name, GeneSymbol, HGNC_ID, ClinicalSignificance, LastEvaluated, nsv/esv (dbVar), RCVaccession, PhenotypeIDS, PhenotypeList, Origin, OriginSimple, Assembly, ChromosomeAccession, ReferenceAllele, Alternate...
dbl (11): #AlleleID, GeneID, ClinSigSimple, RS# (dbSNP), Chromosome, Start, Stop, NumberSubmitters, SubmitterCategories, VariationID, PositionVCF
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
clinvar_hg38_EGFR <-
clinvar_hg38_EGFR %>%
dplyr::select(
-GeneID, -GeneSymbol, -HGNC_ID, -`nsv/esv (dbVar)`,
-Assembly, -ChromosomeAccession, -ReferenceAllele,
-AlternateAllele, -Cytogenetic
) %>%
mutate(var_len = pmax(
str_length(ReferenceAlleleVCF),
str_length(AlternateAlleleVCF)
)) %>%
filter(var_len <= 10) %>%
arrange(PositionVCF)
# only keep main transcript variants
clinvar_hg38_EGFR <-
clinvar_hg38_EGFR %>%
filter(Name != "NM_001346941.2(EGFR):c.89-4536_89-4529del")
clinvar_hg38_EGFR
clinvar_hg38_EGFR_renamed <-
clinvar_hg38_EGFR %>%
dplyr::select(
Name, Start, Stop,
ReferenceAlleleVCF,
AlternateAlleleVCF,
VariationID
) %>%
set_names(c(
"name", "start", "stop",
"ref", "alt", "clinvar_id"
)) %>%
mutate(name = str_replace(
name, "NM_005228.5\\(EGFR\\):",
paste0("g", start, "_")
)) %>%
mutate(clinvar_id = as.character(clinvar_id))
clinvar_hg38_EGFR_renamed
clinvar_hg38_EGFR_not_snv <-
clinvar_hg38_EGFR_renamed %>%
filter(!(str_length(ref) == 1 &
str_length(alt) == 1)) %>%
mutate(type = case_when(
(str_length(ref) == str_length(alt) &
str_length(ref) == 1) ~ "snv",
(str_sub(ref, 1, 1) == alt &
str_length(alt) < str_length(ref)) ~ "del",
(str_sub(alt, 1, 1) == ref &
str_length(ref) == 1 &
str_length(alt) > 1) ~ "ins",
TRUE ~ "indel"
)) %>%
mutate(stop = if_else(type == "ins", stop - 1, stop)) %>%
mutate(
alt = if_else(type == "del", "", alt),
ref = if_else(type == "del", str_sub(ref, 2), ref)
) %>%
rename(clinvar_id = "id") %>%
relocate(type, .after = "name")
clinvar_hg38_EGFR_not_snv
clinvar_hg38_EGFR_snv <-
clinvar_hg38_EGFR_renamed %>%
filter((str_length(ref) == 1 & str_length(alt) == 1))
clinvar_hg38_EGFR_snv
Load COSMIC data
cosmic_dt <-
read_tsv(cosmic_path)
Rows: 15654 Columns: 26── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (20): GENE_SYMBOL, COSMIC_GENE_ID, TRANSCRIPT_ACCESSION, COSMIC_SAMPLE_ID, SAMPLE_NAME, COSMIC_PHENOTYPE_ID, GENOMIC_MUTATION_ID, LEGACY_MUTATION_ID, MUTATION_CDS, MUTATION_AA, MUTATION_DESCRIPTION, MUTATION_ZYGOSIT...
dbl (5): MUTATION_ID, CHROMOSOME, GENOME_START, GENOME_STOP, PUBMED_PMID
lgl (1): LOH
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
cosmic_dt <-
cosmic_dt %>%
filter(TRANSCRIPT_ACCESSION == "ENST00000275493.6") %>%
group_by(GENOMIC_MUTATION_ID) %>%
dplyr::slice(1) %>%
ungroup()
cosmic_dt_subset <-
cosmic_dt %>%
dplyr::select(
MUTATION_CDS, MUTATION_AA,
GENOME_START, GENOME_STOP,
GENOMIC_WT_ALLELE, GENOMIC_MUT_ALLELE,
GENOMIC_MUTATION_ID, MUTATION_DESCRIPTION
) %>%
mutate(MUTATION_AA = str_replace(MUTATION_AA, "p.\\?", "")) %>%
mutate(name = if_else(MUTATION_AA == "",
paste0("g", GENOME_START, "_", MUTATION_CDS),
paste0("g", GENOME_START, "_", MUTATION_CDS, " (", MUTATION_AA, ")")
)) %>%
dplyr::select(
name, GENOME_START, GENOME_STOP,
GENOMIC_WT_ALLELE, GENOMIC_MUT_ALLELE,
GENOMIC_MUTATION_ID
) %>%
set_names(c("name", "start", "stop", "ref", "alt", "cosmic_id")) %>%
mutate(
ref = if_else(is.na(ref), "", ref),
alt = if_else(is.na(alt), "", alt)
) %>%
mutate(var_len = pmax(str_length(ref), str_length(alt))) %>%
filter(var_len <= 10) %>%
dplyr::select(-var_len) %>%
arrange(start)
cosmic_dt_subset_snv <-
cosmic_dt_subset %>%
filter(str_length(ref) == 1 & str_length(alt) == 1)
cosmic_dt_subset_not_snv <-
cosmic_dt_subset %>%
filter(!(str_length(ref) == 1 & str_length(alt) == 1))
cosmic_dt_subset_snv
cosmic_dt_subset_not_snv <-
cosmic_dt_subset_not_snv %>%
mutate(type = case_when(
(str_length(ref) == str_length(alt) & str_length(ref) == 1) ~ "snv",
(str_length(ref) >= 1 & str_length(alt) == 0) ~ "del",
(str_length(ref) == 0 & str_length(alt) >= 1) ~ "ins",
TRUE ~ "indel"
)) %>%
relocate(type, .after = name)
cosmic_dt_subset_not_snv <-
cosmic_dt_subset_not_snv %>%
mutate(extra_base = map_chr(
cosmic_dt_subset_not_snv$start,
~ as.character(subseq(hg_genome[["chr7"]], ., .))
)) %>%
mutate(
stop = if_else(type == "ins", stop - 1, stop),
ref = if_else(type == "ins", extra_base, ref),
alt = if_else(type == "ins", paste0(extra_base, alt), alt)
) %>%
dplyr::select(-extra_base)
cosmic_dt_subset_not_snv
snv_dt <-
full_join(cosmic_dt_subset_snv,
clinvar_hg38_EGFR_snv,
by = c("start", "stop", "ref", "alt")
) %>%
mutate(name = if_else(is.na(name.x), name.y, name.x)) %>%
dplyr::select(
name, start, stop,
ref, alt, cosmic_id,
clinvar_id
) %>%
arrange(start) %>%
mutate(name = gsub("_c\\.\\d+[+-]?\\d+([[:alpha:]])>", "_\\1>", name))
snv_dt
Load variants from base editor experiments
ABE8e_all_dt <- read_csv(ABE8e_path)
New names:Rows: 834 Columns: 29── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (21): sgRNA.sequence, sgRNA.context.sequence, Gene.Symbol, Ensembl.Gene.ID, Ensembl.transcript.ID, Genome.assembly, Transcript.reference.allele, Transcript.alternate.allele, Genome.reference.allele, Genome.alternate...
dbl (8): ...1, Gene.strand, Chromosome, sgrna.genomic.position, X..edits, X.silent.edits, Amino.acid.position_simplified, guide_codon_pos
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
BE4max_all_dt <- read_csv(BE4max_path)
New names:Rows: 834 Columns: 29── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (21): sgRNA.sequence, sgRNA.context.sequence, Gene.Symbol, Ensembl.Gene.ID, Ensembl.transcript.ID, Genome.assembly, Transcript.reference.allele, Transcript.alternate.allele, Genome.reference.allele, Genome.alternate...
dbl (8): ...1, Gene.strand, Chromosome, sgrna.genomic.position, X..edits, X.silent.edits, Amino.acid.position_simplified, guide_codon_pos
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ABE8e_dt <- ABE8e_all_dt %>%
dplyr::select(c(14, 15, 17, 20, 27))
BE4max_dt <- BE4max_all_dt %>%
dplyr::select(c(14, 15, 17, 20, 27))
base_editor_variants <-
bind_rows(BE4max_dt, ABE8e_dt) %>%
dplyr::filter(Mutation.category_simplified %in%
c("Nonsense", "Missense", "Splice-acceptor", "Splice-donor"))
base_editor_variants <-
base_editor_variants %>%
separate_rows(Nucleotide.edits, sep = "C_") %>%
separate_rows(Nucleotide.edits, sep = "A_") %>%
separate_rows(Nucleotide.edits, sep = "_") %>%
separate_rows(Nucleotide.edits, sep = ";") %>%
dplyr::filter(Nucleotide.edits != "") %>%
mutate(Nucleotide.edits = as.integer(Nucleotide.edits)) %>%
mutate(start = if_else(sgRNA.Strand == "sense",
sgrna.genomic.position + (Nucleotide.edits - 1),
sgrna.genomic.position - (Nucleotide.edits - 1)
)) %>%
mutate(
edit =
case_when(
Edit == "C-T" & sgRNA.Strand == "antisense" ~ "G-A",
Edit == "A-G" & sgRNA.Strand == "antisense" ~ "T-C",
TRUE ~ Edit
)
) %>%
dplyr::select(start, edit) %>%
group_by(start) %>%
dplyr::slice(1) %>%
tidyr::separate(edit,
into = c("ref", "alt"), sep = "-"
) %>%
mutate(
stop = start,
name = paste0("g", start, "_", ref, ">", alt)
) %>%
dplyr::select(c("name", "start", "stop", "ref", "alt"))
base_editor_variants
Combine variants data
snv_dt <-
full_join(snv_dt,
base_editor_variants,
by = c("start", "stop", "ref", "alt")
)
snv_dt <-
snv_dt %>%
mutate(name = if_else(is.na(name.x), name.y, name.x)) %>%
rename(name.y = "be_id") %>%
dplyr::select(
name, start, stop, ref,
alt, cosmic_id, clinvar_id, be_id
) %>%
unite("id", cosmic_id:be_id, sep = ";", na.rm = TRUE) %>%
arrange(start) %>%
mutate(type = "snv")
snv_dt
non_snv_dt <-
full_join(clinvar_hg38_EGFR_not_snv,
cosmic_dt_subset_not_snv,
by = c("start", "stop", "ref", "alt", "type")
) %>%
mutate(name = if_else(is.na(name.x), name.y, name.x)) %>%
dplyr::select(
name, type, start, stop,
ref, alt, cosmic_id,
id
) %>%
arrange(start) %>%
unite("id", cosmic_id:id, sep = ";", na.rm = TRUE)
non_snv_dt
variants_dt <-
bind_rows(
snv_dt,
non_snv_dt
) %>%
arrange(start) %>%
mutate(name = sub(" \\(p\\..*$", "", name))
variants_dt
Annotate Variants
annotate_and_predict_variants <- function(variants_dt) {
seq_len_min <- 49
num_variants <- NROW(variants_dt)
gr0 <-
GRanges(
Rle(c("chr7"), num_variants),
IRanges(
start = variants_dt$start,
end = variants_dt$stop,
names = variants_dt$name
)
)
var_allelles <- DNAStringSet(variants_dt$alt)
intron_locations <-
locateVariants(gr0,
intbytx,
IntronVariants(),
varAllele = var_allelles
)
# Locate intronic variants
intron_locations <-
intron_locations %>%
as_tibble() %>%
add_column(name = names(intron_locations)) %>%
relocate(name, .before = "seqnames") %>%
dplyr::select(-PRECEDEID, -FOLLOWID, -TXID)
# Locate remaining variants
all <- locateVariants(gr0,
txdb,
AllVariants(),
varAllele = var_allelles
)
all <-
all %>%
as_tibble() %>%
add_column(name = names(all)) %>%
relocate(name, .before = "seqnames") %>%
dplyr::select(-PRECEDEID, -FOLLOWID)
# Flag intronic and 5UTR variants that have alternative
# effects given other potential spliced transcripts
transcript_splice_sites <-
all %>%
dplyr::filter(LOCATION == "spliceSite" & TXID == "93502")
intron_locations <-
intron_locations %>%
filter(!(name %in% transcript_splice_sites$name))
uncertain_introns <-
all %>%
dplyr::filter(name %in% intron_locations$name) %>%
group_by(name, LOCATION) %>%
dplyr::slice(1) %>%
ungroup() %>%
group_by(name) %>%
mutate(frac_intron = sum(LOCATION == "intron") / n()) %>%
dplyr::filter(frac_intron < 1) %>%
dplyr::filter(LOCATION != "intron") %>%
dplyr::filter(TXID != 93502) %>%
arrange(QUERYID) %>%
dplyr::select(-GENEID, -frac_intron) %>%
ungroup()
flag_variants <-
uncertain_introns %>%
dplyr::select(name, LOCATION, TXID) %>%
left_join(., tx_lengths %>%
dplyr::select(tx_id, tx_name) %>%
mutate(tx_id = as.character(tx_id)) %>%
dplyr::rename(TXID = "tx_id"),
by = "TXID"
) %>%
dplyr::select(-TXID) %>%
unite("alt_effect", LOCATION:tx_name)
all <-
all %>%
dplyr::filter(TXID == 93502) %>%
dplyr::select(-TXID)
potential_promoters <-
all[which(duplicated(all$name)), ] %>%
dplyr::select(name) %>%
mutate(alt_effect = "promoter_ENST00000275493.7")
flag_variants <-
bind_rows(flag_variants, potential_promoters)
flag_variants <-
flag_variants %>%
group_by(name) %>%
dplyr::slice(1) %>%
ungroup()
all <-
all %>%
dplyr::filter(!duplicated(all$name))
all <-
bind_rows(
all,
intron_locations
)
all <-
left_join(all, flag_variants, by = "name") %>%
dplyr::select(-GENEID)
all <-
all %>%
dplyr::select(-c(LOCSTART:CDSID))
gene_model_gr_list <-
as(gene_model_gr, "GRangesList")
names(gene_model_gr_list) <- mcols(gene_model_gr)$id
map_grs <-
mapToTranscripts(resize(gr0, 1), gene_model_gr_list)
map_dt <-
map_grs %>%
as_tibble() %>%
mutate(name = names(map_grs)) %>%
dplyr::select(name, seqnames, start) %>%
set_names(c("name", "loc", "loc_start")) %>%
left_join(., loc_lens, by = "loc")
all <-
left_join(all,
map_dt,
by = "name"
)
# Predict consequence of variant
predict_coding_variants <-
predictCoding(gr0, txdb, hg_genome, varAllele = var_allelles)
predict_coding_variants <-
predict_coding_variants[mcols(predict_coding_variants)$TXID == 93502]
predict_coding_variants <-
as_tibble(predict_coding_variants) %>%
add_column(name = names(predict_coding_variants)) %>%
dplyr::select(
name, CDSLOC.start, CONSEQUENCE, REFCODON,
VARCODON, REFAA, VARAA, PROTEINLOC
)
n_distinct(all$name)
all <-
left_join(all, predict_coding_variants, by = "name")
all <-
all %>%
mutate(loc_end_comp = loc_len - (loc_start + width - 1)) %>%
mutate(boundary_flag = (LOCATION == "coding" &
(loc_start < seq_len_min | loc_end_comp < seq_len_min)))
all <-
all %>%
dplyr::select(
name, CONSEQUENCE, LOCATION, loc, loc_len, loc_start,
CDSLOC.start, PROTEINLOC, REFCODON, VARCODON,
REFAA, VARAA, boundary_flag, alt_effect
) %>%
set_names(c(
"name", "consequence", "location", "region", "region_len",
"pos_region", "pos_cds", "pos_protein", "ref_codon",
"var_codon", "ref_aa", "var_aa", "boundary_flag", "alt_effect"
)) %>%
rowwise() %>%
mutate(pos_protein = str_c(pos_protein, collapse = ":"))
return(all)
}
all <-
annotate_and_predict_variants(variants_dt)
Warning: GRanges object contains 20914 out-of-bound ranges located on sequences 93501, 93502, 93503, 93504, 93505, 93509, and 93510. Note that ranges located on a sequence whose length is unknown (NA) or on a
circular sequence are not considered out-of-bound (use seqlengths() and isCircular() to get the lengths and circularity flags of the underlying sequences). You can use trim() to trim these ranges.
See ?`trim,GenomicRanges-method` for more information.'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
Warning: GRanges object contains 20914 out-of-bound ranges located on sequences 93501, 93502, 93503, 93504, 93505, 93509, and 93510. Note that ranges located on a sequence whose length is unknown (NA) or on a
circular sequence are not considered out-of-bound (use seqlengths() and isCircular() to get the lengths and circularity flags of the underlying sequences). You can use trim() to trim these ranges.
See ?`trim,GenomicRanges-method` for more information.Warning: records with missing 'varAllele' were ignoredWarning: in 'x[[12817]]': last 2 bases were ignoredWarning: in 'x[[12817]]': last 2 bases were ignored
Include base editing variants generated by two or more edits
min_base_changes <- function(target_amino_acid, ref_codon) {
# Define a mapping of amino acids to codons
amino_acid_mapping <- list(
"A" = c("GCT", "GCC", "GCA", "GCG"),
"R" = c("CGT", "CGC", "CGA", "CGG", "AGA", "AGG"),
"N" = c("AAT", "AAC"),
"D" = c("GAT", "GAC"),
"C" = c("TGT", "TGC"),
"Q" = c("CAA", "CAG"),
"E" = c("GAA", "GAG"),
"G" = c("GGT", "GGC", "GGA", "GGG"),
"H" = c("CAT", "CAC"),
"I" = c("ATA"), # "ATT", "ATC", two A>Ts
"L" = c("TTA", "TTG", "CTT", "CTC", "CTA", "CTG"),
"K" = c("AAA", "AAG"),
"M" = c("ATG"),
"F" = c("TTT", "TTC"),
"P" = c("CCT", "CCC", "CCA", "CCG"),
"S" = c("TCT", "TCC", "TCA", "TCG", "AGT", "AGC"),
"T" = c("ACT", "ACC", "ACA", "ACG"),
"W" = c("TGG"),
"Y" = c("TAT", "TAC"),
"V" = c("GTT", "GTC", "GTA", "GTG"),
"*" = c("TAA", "TAG", "TGA")
)
# Find the reference amino acid corresponding to the reference codon
ref_amino_acid <-
names(amino_acid_mapping)[which(ref_codon %in% unlist(amino_acid_mapping))]
# Check if the reference amino acid is valid
if (is.null(ref_amino_acid)) {
stop("Invalid reference codon.")
}
# Check if the target amino acid is valid
if (!(target_amino_acid %in% names(amino_acid_mapping))) {
stop("Invalid target amino acid.")
}
# Find the target codons for the given amino acid
target_codons <- amino_acid_mapping[[target_amino_acid]]
# Calculate the number of base changes needed for each target codon
base_changes <-
sapply(
target_codons,
function(tc) sum(strsplit(ref_codon, "")[[1]] != strsplit(tc, "")[[1]])
)
# Find the index of the minimum base changes
min_index <-
which.min(base_changes)
# Return the variant codon with minimum base changes
return(target_codons[min_index])
}
get_codon_at_aa_position <- function(pos) {
start_pos <- pos * 3 - 2
end_pos <- pos * 3
as.character(subseq(EGFR_seq, start_pos, end_pos))
}
ABE8e_aa_var <-
ABE8e_all_dt %>%
dplyr::select(Amino.acid.edits) %>%
separate_rows(Amino.acid.edits, sep = ";") %>%
dplyr::filter(!(Amino.acid.edits %in% c("utr", "None", ""))) %>%
dplyr::filter(!str_detect(Amino.acid.edits, "Exon")) %>%
separate_wider_regex(Amino.acid.edits,
c(
ref_aa = "^[A-Za-z]+",
pos_protein = "[0-9]+",
var_aa = "[A-Za-z]+$"
),
cols_remove = F
) %>%
dplyr::filter(ref_aa != var_aa)
BE4max_aa_var <-
BE4max_all_dt %>%
dplyr::select(Amino.acid.edits) %>%
separate_rows(Amino.acid.edits, sep = ";") %>%
dplyr::filter(!(Amino.acid.edits %in% c("utr", "None", ""))) %>%
dplyr::filter(!str_detect(Amino.acid.edits, "Exon")) %>%
separate_wider_regex(Amino.acid.edits,
c(
ref_aa = "^[A-Za-z]+",
pos_protein = "[0-9]+",
var_aa = "[A-Za-z]+$"
),
cols_remove = F
) %>%
dplyr::filter(ref_aa != var_aa)
aa_map <- c(AMINO_ACID_CODE, "*" = "Ter")
flipped_aa_map <- setNames(names(aa_map), aa_map)
be_var_dt <-
bind_rows(ABE8e_aa_var, BE4max_aa_var) %>%
group_by(Amino.acid.edits) %>%
dplyr::slice(1) %>%
ungroup() %>%
mutate(
ref_aa = flipped_aa_map[ref_aa],
var_aa = flipped_aa_map[var_aa]
)
variants_dt0 <-
left_join(variants_dt, all, by = "name")
be_var_dt <-
left_join(be_var_dt,
variants_dt0 %>%
filter(pos_protein != "" & type == "snv") %>%
dplyr::select(name, ref_aa, pos_protein, var_aa),
by = c("ref_aa", "pos_protein", "var_aa")
) %>%
dplyr::filter(is.na(name)) %>%
mutate(pos_protein = as.numeric(pos_protein)) %>%
rowwise() %>%
mutate(ref_codon = get_codon_at_aa_position(pos_protein))
be_var_dt <-
be_var_dt %>%
mutate(var_codon = min_base_changes(var_aa, ref_codon))
be_var_dt <-
be_var_dt %>%
mutate(
last_base_diff =
str_sub(ref_codon, 3, 3) != str_sub(var_codon, 3, 3)
) %>%
mutate(
ref = if_else(last_base_diff, ref_codon, str_sub(ref_codon, 1, 2)),
alt = if_else(last_base_diff, var_codon, str_sub(var_codon, 1, 2)),
start = ref_locs[pos_protein * 3 - 2],
stop = if_else(last_base_diff, start + 2, start + 1)
) %>%
mutate(name = paste0("g", start, "_", ref, ">", alt)) %>%
mutate(
id = name,
type = "indel"
) %>%
dplyr::select(c("name", "type", "start", "stop", "ref", "alt", "id"))
rm(variants_dt0)
# remove g55174776_TT>CC since it is the same as g55174776_c.2239_2240delinsCC
be_var_dt <-
be_var_dt %>%
filter(name != "g55174776_TT>CC")
be_var_dt
Include double base edits variants
variants_dt <-
bind_rows(variants_dt, be_var_dt) %>%
arrange(start)
Reannotate after inclusion of double base edits variants
all <-
annotate_and_predict_variants(variants_dt)
Warning: GRanges object contains 21739 out-of-bound ranges located on sequences 93501, 93502, 93503, 93504, 93505, 93509, and 93510. Note that ranges located on a sequence whose length is unknown (NA) or on a
circular sequence are not considered out-of-bound (use seqlengths() and isCircular() to get the lengths and circularity flags of the underlying sequences). You can use trim() to trim these ranges.
See ?`trim,GenomicRanges-method` for more information.'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
'select()' returned many:1 mapping between keys and columns
Warning: GRanges object contains 21739 out-of-bound ranges located on sequences 93501, 93502, 93503, 93504, 93505, 93509, and 93510. Note that ranges located on a sequence whose length is unknown (NA) or on a
circular sequence are not considered out-of-bound (use seqlengths() and isCircular() to get the lengths and circularity flags of the underlying sequences). You can use trim() to trim these ranges.
See ?`trim,GenomicRanges-method` for more information.Warning: records with missing 'varAllele' were ignoredWarning: in 'x[[13259]]': last 2 bases were ignoredWarning: in 'x[[13259]]': last 2 bases were ignored
variants_dt <-
left_join(variants_dt, all, by = "name")
variants_dt
table(variants_dt$location)
spliceSite intron fiveUTR threeUTR coding intergenic promoter
65 1825 5 61 3060 0 0
table(variants_dt$location, variants_dt$type)
del indel ins snv
spliceSite 1 0 2 62
intron 66 3 38 1718
fiveUTR 0 0 0 5
threeUTR 5 0 3 53
coding 27 127 35 2871
intergenic 0 0 0 0
promoter 0 0 0 0
Filter out introns
variants_dt <-
variants_dt %>%
filter(location != c("intron"))
variants_dt
Generate synonymous mutations near target Single Nucleotide
Variants
# Function to split a string into consecutive triplets
split_into_triplets <- function(s) {
n <- nchar(s)
if (n %% 3 != 0) {
warning("String length is not a multiple of 3. Padding with spaces.")
s <- paste0(s, rep(" ", 3 - (n %% 3)))
}
matrix(strsplit(s, "")[[1]], ncol = 3, byrow = TRUE)
}
# Split the vector into triplets
triplets <- split_into_triplets(as.character(EGFR_seq))
# Create a dataframe
codon_map_df <-
tibble(ref_codon = apply(triplets, 1, paste, collapse = "")) %>%
mutate(pos_protein = row_number())
codon_map_df
syn_codon_map <-
read_delim(syn_codon_path)
Rows: 59 Columns: 5── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: " "
chr (5): ref_aa, ref_codon, var_codon, ref, alt
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
syn_codon_map
codon_map_df <-
left_join(codon_map_df, syn_codon_map, by = "ref_codon") %>%
mutate(start = ref_locs[pos_protein * 3]) %>%
filter(!is.na(ref_aa))
codon_map_df
Split SNVS into spliceSite coding, and intron variants
variants_snv <-
variants_dt %>%
filter(type == "snv")
variants_snv_coding <-
variants_snv %>%
filter(location %in% c("coding"))
Find nonsynomous mutations
get_codon_map <- function(start, name, num_idx) {
dis <- abs(start - codon_map_df$start)
dis[dis <= 2] <- 10000
lowest_indices <- head(order(dis), num_idx)
bp_dist <- codon_map_df$start[lowest_indices] - start
dt <- tibble(name = name, dist = bp_dist)
dt <- bind_cols(dt, codon_map_df[lowest_indices, ])
return(dt)
}
coding_syn <-
map2_df(variants_snv_coding$start,
variants_snv_coding$name,
get_codon_map,
num_idx = 1
) %>%
rename(name = "target_name") %>%
mutate(name = paste0("g", start, "_", ref, ">", alt)) %>%
dplyr::select(
name, start, ref, alt, ref,
target_name, pos_protein,
ref_codon, var_codon, ref_aa, dist
)
table(coding_syn$dist)
-8 -7 -6 -5 -4 -3 3 4 5 6 7 8
1 4 4 54 910 955 38 845 53 1 4 2
coding_syn <-
coding_syn %>%
dplyr::select(-dist) %>%
group_by(name) %>%
mutate(target_name = paste(target_name, collapse = ";")) %>%
dplyr::slice(1)
coding_syn
## Previous code for including intronic variants!!
# variants_snv_intron <-
# variants_snv %>%
# filter(location %in% c("intron", "spliceSite"))
# mut_pos_dist <- 3
# intron_syn <-
# variants_snv_intron %>%
# dplyr::select(name, start, region_len, pos_region) %>%
# mutate(pos_region_comp = region_len - pos_region) %>%
# mutate(upstream = case_when(
# pos_region_comp < 8 ~ F,
# pos_region_comp >= 8 ~ T
# )) %>%
# mutate(
# start_syn = if_else(upstream, start + mut_pos_dist, start - mut_pos_dist),
# ref = map_chr(
# start_syn,
# ~ as.character(subseq(hg_genome[["chr7"]], ., .))
# )
# )
#
# dup_alternatives <-
# intron_syn %>%
# filter(start %in% intron_syn$start_syn) %>%
# separate(name, into = c("edit", "alt0"), remove = F, sep = ">") %>%
# separate(edit, into = c("loc", "ref0"), remove = F, sep = "_") %>%
# dplyr::select(name, start, ref0, alt0) %>%
# group_by(start) %>%
# mutate(bases = paste(alt0, collapse = ",")) %>%
# ungroup() %>%
# mutate(bases = paste0(bases, ",", ref0)) %>%
# rowwise() %>%
# mutate(alt_2 = setdiff(c("A", "C", "T", "G"),
# str_split(bases, pattern = ",")[[1]])[1]) %>%
# mutate(name_sym = paste0("g", start, "_", ref0, ">", alt_2)) %>%
# dplyr::select(name, name_sym, alt_2)
# dup_alternatives
# intron_syn <-
# intron_syn %>%
# mutate(alt = case_when(
# ref == "G" ~ "A",
# ref == "A" ~ "G",
# ref == "C" ~ "T",
# ref == "T" ~ "C"
# )) %>%
# dplyr::select(name, start_syn, ref, alt) %>%
# dplyr::rename(target_name = "name", start = "start_syn") %>%
# mutate(name = paste0("g", start, "_", ref, ">", alt)) %>%
# dplyr::select(name, start, ref, alt, target_name) %>%
# group_by(name) %>%
# mutate(target_name = paste(target_name, collapse = ";")) %>%
# dplyr::slice(1)
# intron_syn <-
# left_join(intron_syn,
# dup_alternatives,
# by = "name"
# ) %>%
# mutate(
# name = if_else(is.na(name_sym), name, name_sym),
# alt = if_else(is.na(alt_2), alt, alt_2)
# ) %>%
# dplyr::select(-name_sym, -alt_2)
# syn_variants <-
# bind_rows(
# coding_syn %>%
# mutate(location = "coding"),
# intron_syn %>%
# mutate(
# pos_protein = NA,
# ref_codon = NA,
# var_codon = NA,
# ref_aa = NA,
# location = "intron"
# )
# )
# syn_variants
syn_variants <-
left_join(
coding_syn %>%
mutate(location = "coding"),
variants_snv %>%
dplyr::select(name, id),
by = "name"
) %>%
mutate(
syn_in_target = name %in% variants_dt$name,
num_targets = str_count(target_name, ";") + 1
)
syn_variants
syn_target_map <-
syn_variants %>%
group_by(num_targets, syn_in_target) %>%
tally()
syn_target_map
syn_variants_long <-
syn_variants %>%
separate_rows(target_name, sep = ";") %>%
dplyr::select(
target_name, name, start, ref,
alt
) %>%
set_names(c(
"name", "name_syn", "start_syn",
"ref_syn", "alt_syn"
))
syn_variants_long
variants_dt <-
left_join(variants_dt, syn_variants_long, by = "name")
variants_dt
variants_dt <-
variants_dt %>%
mutate(target_in_syn = if_else(name %in% syn_variants$name, T, F)) %>%
mutate(name_target_syn = paste0(name, ":", name_syn)) %>%
mutate(
clinvar = if_else(grepl("\\b\\d+\\b", id), T, F),
cosmic = if_else(grepl("COSV", id), T, F),
base_edit = if_else(grepl("g\\d+_[A-Z]+>[A-Z]+", id), T, F)
) %>%
dplyr::select(
name, id, ref, alt, ref_aa, var_aa, pos_protein,
type, consequence, location, name_syn, name_target_syn,
target_in_syn, alt_effect,
clinvar, cosmic, base_edit, start, stop, start_syn, everything()
) %>%
mutate(name_target_syn=if_else(is.na(name_syn), NA, name_target_syn))
variants_dt
Split variants into target, target_syn, and syn
variants_snv <-
variants_dt %>%
filter(!is.na(name_syn))
target_syn_variants <-
variants_snv %>%
dplyr::select(name_target_syn, name_syn, name) %>%
set_names(c("target_name","name_syn", "name_target")) %>%
mutate( name_target_syn=NA,
num_targets=NA,
paired_targets_as_syn= NA,
paired_targets_syn_as_syn= NA) %>%
dplyr::select(target_name, name_target, name_syn,
name_target_syn,
num_targets, paired_targets_as_syn,
paired_targets_syn_as_syn )
target_syn_variants
NA
syn_variants_dt <-
variants_snv %>%
dplyr::select(name_syn, name, name_target_syn) %>%
set_names(c("target_name", "name_target", "name_target_syn" )) %>%
group_by(target_name)%>%
mutate(name_target = paste(name_target, collapse = ";"),
name_target_syn = paste(name_target_syn, collapse = ";")) %>%
dplyr::slice(1) %>%
mutate(
syn_in_target = target_name %in% variants_dt$name,
num_targets = str_count(name_target, ";") + 1
)
syn_variants_dt_filtered <-
syn_variants_dt %>%
mutate(target_in_syn=NA,
name_syn=NA,
paired_targets_as_syn= NA,
paired_targets_syn_as_syn= NA) %>%
filter(!(syn_in_target))%>%
dplyr::select(target_name, name_target, name_syn,
name_target_syn,
num_targets,
paired_targets_as_syn,
paired_targets_syn_as_syn )
syn_variants_in_target <-
syn_variants_dt %>%
filter((syn_in_target)) %>%
dplyr::select(-syn_in_target) %>%
rename(name_target="paired_targets_as_syn",
name_target_syn="paired_targets_syn_as_syn")
syn_variants_dt_filtered
variants_dt <-
left_join(variants_dt,
syn_variants_in_target %>%
rename(target_name="name"),
by="name")
variants_dt
target_variants <-
variants_dt %>%
dplyr::select(name, name_syn, name_target_syn, paired_targets_as_syn, paired_targets_syn_as_syn, num_targets) %>%
set_names(c("target_name", "name_syn", "name_target_syn", "paired_targets_as_syn", "paired_targets_syn_as_syn", "num_targets")) %>%
mutate(name_target=NA) %>%
dplyr::select(target_name, name_target, name_syn,
name_target_syn,
num_targets,
paired_targets_as_syn,
paired_targets_syn_as_syn )
target_variants
all_variants_paired <-
bind_rows(target_variants,
target_syn_variants,
syn_variants_dt_filtered)
all_variants_paired
Save to files
write_tsv(
all_variants_paired,
all_variants_paired_path
)
write_tsv(
variants_dt, variants_table_path)
sessionInfo()
R version 4.3.2 (2023-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.6 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Europe/Zurich
tzcode source: system (glibc)
attached base packages:
[1] stats4 stats graphics grDevices utils datasets methods base
other attached packages:
[1] VariantAnnotation_1.48.1 Rsamtools_2.18.0 SummarizedExperiment_1.32.0 MatrixGenerics_1.14.0 matrixStats_1.2.0
[6] org.Hs.eg.db_3.18.0 BSgenome.Hsapiens.UCSC.hg38_1.4.5 BSgenome_1.70.1 rtracklayer_1.62.0 BiocIO_1.12.0
[11] Biostrings_2.70.1 XVector_0.42.0 TxDb.Hsapiens.UCSC.hg38.knownGene_3.18.0 GenomicFeatures_1.54.1 AnnotationDbi_1.64.1
[16] Biobase_2.62.0 GenomicRanges_1.54.1 GenomeInfoDb_1.38.5 IRanges_2.36.0 S4Vectors_0.40.2
[21] BiocGenerics_0.48.1 lubridate_1.9.3 forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4
[26] purrr_1.0.2 readr_2.1.5 tidyr_1.3.0 tibble_3.2.1 ggplot2_3.4.4
[31] tidyverse_2.0.0
loaded via a namespace (and not attached):
[1] tidyselect_1.2.0 blob_1.2.4 filelock_1.0.3 bitops_1.0-7 fastmap_1.1.1 RCurl_1.98-1.14 BiocFileCache_2.10.1 GenomicAlignments_1.38.1
[9] XML_3.99-0.16 digest_0.6.34 timechange_0.2.0 lifecycle_1.0.4 KEGGREST_1.42.0 RSQLite_2.3.4 magrittr_2.0.3 compiler_4.3.2
[17] rlang_1.1.3 progress_1.2.3 tools_4.3.2 utf8_1.2.4 yaml_2.3.8 knitr_1.45 S4Arrays_1.2.0 prettyunits_1.2.0
[25] bit_4.0.5 curl_5.2.0 DelayedArray_0.28.0 xml2_1.3.6 abind_1.4-5 BiocParallel_1.36.0 withr_2.5.2 grid_4.3.2
[33] fansi_1.0.6 colorspace_2.1-0 scales_1.3.0 biomaRt_2.58.0 cli_3.6.2 crayon_1.5.2 generics_0.1.3 rstudioapi_0.15.0
[41] httr_1.4.7 tzdb_0.4.0 rjson_0.2.21 DBI_1.2.1 cachem_1.0.8 zlibbioc_1.48.0 parallel_4.3.2 restfulr_0.0.15
[49] vctrs_0.6.5 Matrix_1.6-5 hms_1.1.3 bit64_4.0.5 glue_1.7.0 codetools_0.2-19 stringi_1.8.3 gtable_0.3.4
[57] munsell_0.5.0 pillar_1.9.0 rappdirs_0.3.3 GenomeInfoDbData_1.2.11 R6_2.5.1 dbplyr_2.4.0 vroom_1.6.5 lattice_0.22-5
[65] png_0.1-8 memoise_2.0.1 SparseArray_1.2.3 xfun_0.41 pkgconfig_2.0.3
LS0tCnRpdGxlOiAiRUdGUiBQcmltZSBFZGl0aW5nIExpYnJhcnkiCnN1YnRpdGxlOiAiUGFydCAxOiBDb21iaW5lIFZhcmlhbnRzIGFuZCBDcmVhdGUgVGFibGUiCmF1dGhvcjogCi0gbmFtZTogUmljayBGYXJvdW5pCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVZLSVCLSVkIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0b2M6IG5vCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKCgojIExvYWQgbGlicmFyaWVzIGFuZCBzZXQgZmlsZXBhdGhzIAoKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShUeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShCU2dlbm9tZS5Ic2FwaWVucy5VQ1NDLmhnMzgpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShvcmcuSHMuZWcuZGIpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShWYXJpYW50QW5ub3RhdGlvbikpCmBgYAoKCmBgYHtyfQpkYl9wYXRoIDwtICIuL2lucHV0IgpvdXRwdXRfZGlyIDwtICIuL291dHB1dCIKIyBmdHBfcGF0aCA8LSAiZnRwOi8vZnRwLm5jYmkubmxtLm5paC5nb3YvcHViL2NsaW52YXIvdGFiX2RlbGltaXRlZC92YXJpYW50X3N1bW1hcnkudHh0Lmd6IgoKY2xpbnZhcl9FR0ZSX3BhdGggPC0KICBmaWxlLnBhdGgoZGJfcGF0aCwgInZhcmlhbnRfc3VtbWFyeV9HUkNoMzhfRUdGUi50c3YiKQoKY29zbWljX3BhdGggPC0KICBmaWxlLnBhdGgoZGJfcGF0aCwgIkNvc21pY19HZW5vbWVTY3JlZW5zTXV0YW50X3Y5OV9HUkNoMzhfRUdGUi50c3YiKQoKQUJFOGVfcGF0aCA8LQogIGZpbGUucGF0aChkYl9wYXRoLCAic2dybmFfZGVzaWduc19BQkU4ZS5jc3YiKQpCRTRtYXhfcGF0aCA8LQogIGZpbGUucGF0aChkYl9wYXRoLCAic2dybmFfZGVzaWduc19CRTRtYXguY3N2IikKc3luX2NvZG9uX3BhdGggPC0KICBmaWxlLnBhdGgoZGJfcGF0aCwgInN5bl9jb2RvbnMudHh0IikKCnZhcmlhbnRzX3RhYmxlX3BhdGggPC0KICBmaWxlLnBhdGgob3V0cHV0X2RpciwgInZhcmlhbnRzX3RhYmxlLnRzdiIpCgphbGxfdmFyaWFudHNfcGFpcmVkX3BhdGggPC0gIAogIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiYWxsX3ZhcmlhbnRzX3BhaXJlZC50c3YiKQpgYGAKCiMjIEV4dHJhY3QgY2xpbnZhciBkYXRhIGFuZCBzYXZlIHRvIGZpbGUgZm9yIGxhdGVyIHByb2Nlc3NpbmcKCmBgYHtyfQojICMgcnVuIG9uY2UKIyBjbGludmFyX2FsbF9wYXRoIDwtCiMgICBmaWxlLnBhdGgoZGJfcGF0aCwgInZhcmlhbnRfc3VtbWFyeS50eHQuZ3oiKQojCiMKIyBjbGludmFyX2FsbCA8LSByZWFkX3RzdihnemZpbGUoY2xpbnZhcl9hbGxfcGF0aCkpCiMKIyBjbGludmFyX0VHRlIgPC0KIyAgIGNsaW52YXJfYWxsICU+JQojICAgZmlsdGVyKEdlbmVTeW1ib2wgPT0gIkVHRlIiICYKIyAgICAgQXNzZW1ibHkgPT0gIkdSQ2gzOCIgJgojICAgICAhKFR5cGUgJWluJSBjKCJNaWNyb3NhdGVsbGl0ZSIsICJJbnZlcnNpb24iKSkgJgojICAgICBTdGFydCA+PSA1NTAxOTAxNykKIyB3cml0ZV90c3YoY2xpbnZhcl9FR0ZSLCBjbGludmFyX0VHRlJfcGF0aCkKYGBgCgojIENyZWF0ZSBHZW5lIG1vZGVsIGZvciBFR0ZSLTIwMSAoRU5TVDAwMDAwMjc1NDkzLjcpCgpgYGB7cn0KaGdfZ2Vub21lIDwtIEJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGczOApgYGAKCmBgYHtyfQp0eGRiIDwtIFR4RGIuSHNhcGllbnMuVUNTQy5oZzM4Lmtub3duR2VuZQp0eGRiIDwtIGtlZXBTZXFsZXZlbHModHhkYiwgImNocjciKQpgYGAKCgoKYGBge3J9CnR4X2xlbmd0aHMgPC0KICB0cmFuc2NyaXB0TGVuZ3Rocyh0eGRiLAogICAgd2l0aC51dHI1X2xlbiA9IFRSVUUsCiAgICB3aXRoLnV0cjNfbGVuID0gVFJVRQogICkKCnR4X2xlbmd0aHMgPC0KICB0eF9sZW5ndGhzICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VuZV9pZCA9PSAxOTU2KSAlPiUKICBhcnJhbmdlKC1uZXhvbikKdHhfbGVuZ3RocwpgYGAKCgpgYGB7cn0KKHR4X2xlbmd0aHMkdHhfbGVuIC0gKHR4X2xlbmd0aHMkdXRyNV9sZW4gKyB0eF9sZW5ndGhzJHV0cjNfbGVuKSkgLyAzCmBgYAoKIyMjIEdldCB0aGUgY2RzIHBhcnRzLCB0aGUgaW50cm9ucywgdGhlIDNVVFIsIGFuZCB0aGUgNVVUUiBhbmQgY29tYmluZQoKCmBgYHtyfQpjZHNfdHggPC0gY2RzQnkodHhkYiwgYnkgPSAidHgiLCB1c2UubmFtZXMgPSBUUlVFKQpjZHNfdHggPC0gY2RzX3R4WyJFTlNUMDAwMDAyNzU0OTMuNyJdCgppbnRieXR4IDwtIGludHJvbnNCeVRyYW5zY3JpcHQodHhkYiwgdXNlLm5hbWVzID0gVFJVRSkKaW50Ynl0eCA8LSBpbnRieXR4WyJFTlNUMDAwMDAyNzU0OTMuNyJdCgoKZml2ZV91dHIgPC0gZml2ZVVUUnNCeVRyYW5zY3JpcHQodHhkYiwgdXNlLm5hbWVzID0gVFJVRSkkRU5TVDAwMDAwMjc1NDkzLjcKdGhyZWVfdXRyIDwtIHRocmVlVVRSc0J5VHJhbnNjcmlwdCh0eGRiLCB1c2UubmFtZXMgPSBUUlVFKSRFTlNUMDAwMDAyNzU0OTMuNwpnZW5lX21vZGVsX2dyIDwtCiAgYyhmaXZlX3V0ciwgY2RzX3R4JEVOU1QwMDAwMDI3NTQ5My43LCBpbnRieXR4JEVOU1QwMDAwMDI3NTQ5My43LCB0aHJlZV91dHIpCmdlbmVfbW9kZWxfZ3IgPC0gc29ydChnZW5lX21vZGVsX2dyKQppZF9jb2wgPC0gbWNvbHMoZ2VuZV9tb2RlbF9ncikkY2RzX2lkCmlkX2NvbFsxXSA8LSAiNXV0cl8yNDcyNzkiCmlkX2NvbFs1N10gPC0gIjN1dHJfMjQ3MzMwIgppZF9jb2xbc2VxKDIsIDU2LCAyKV0gPC0gcGFzdGUwKCJjZHMiLCAxOjI4LCAiXyIsIGlkX2NvbFtzZXEoMiwgNTYsIDIpXSkKaWRfY29sW3NlcSgzLCA1NSwgMildIDwtIHBhc3RlMCgiaW50cm9uIiwgMToyNykKbWNvbHMoZ2VuZV9tb2RlbF9ncikgPC0gRGF0YUZyYW1lKGlkID0gaWRfY29sKQpjZHNfc2VxcyA8LSBleHRyYWN0VHJhbnNjcmlwdFNlcXMoaGdfZ2Vub21lLCBhcyhnZW5lX21vZGVsX2dyLCAiR1Jhbmdlc0xpc3QiKSkKbWNvbHMoZ2VuZV9tb2RlbF9ncikkc2VxIDwtIGNkc19zZXFzCmdlbmVfbW9kZWxfZ3IKYGBgCgoKYGBge3J9CmxvY19sZW5zIDwtCiAgdGliYmxlKGxvYyA9IG1jb2xzKGdlbmVfbW9kZWxfZ3IpJGlkLCBsb2NfbGVuID0gd2lkdGgoZ2VuZV9tb2RlbF9ncikpCmxvY19sZW5zCmBgYAoKCgpgYGB7cn0KRUdGUl9zZXEgPC0gZXh0cmFjdFRyYW5zY3JpcHRTZXFzKGhnX2dlbm9tZSwgY2RzX3R4KQpFR0ZSX3NlcQpgYGAKCmBgYHtyfQpFR0ZSX2FhIDwtIHRyYW5zbGF0ZShFR0ZSX3NlcSkKRUdGUl9hYQpgYGAKCgpgYGB7cn0KY2RzX3N0YXJ0cyA8LSBzdGFydChjZHNfdHgpCmNkc19lbmRzIDwtIGVuZChjZHNfdHgpCmNkc193aWR0aCA8LQogIHRyYW5zY3JpcHRXaWR0aHMoCiAgICBleG9uU3RhcnRzID0gY2RzX3N0YXJ0cywKICAgIGV4b25FbmRzID0gY2RzX2VuZHMKICApCnJlZl9sb2NzIDwtIHRyYW5zY3JpcHRMb2NzMnJlZkxvY3MobGlzdChjKDE6Y2RzX3dpZHRoKSksCiAgZXhvblN0YXJ0cyA9IGNkc19zdGFydHMsCiAgZXhvbkVuZHMgPSBjZHNfZW5kcywKICBzdHJhbmQgPSBjKCIrIikKKVtbMV1dCnJlZl9sb2NzWzE6MTBdCmBgYAoKCiMgTG9hZCwgY29tYmluZSwgYW5kIGZpbHRlciB2YXJpYW50IGRhdGEKCiMjIExvYWQgY2xpbnZhciBkYXRhCgpgYGB7cn0KY2xpbnZhcl9oZzM4X0VHRlIgPC0KICByZWFkX3RzdihjbGludmFyX0VHRlJfcGF0aCkKCmNsaW52YXJfaGczOF9FR0ZSIDwtCiAgY2xpbnZhcl9oZzM4X0VHRlIgJT4lCiAgZHBseXI6OnNlbGVjdCgKICAgIC1HZW5lSUQsIC1HZW5lU3ltYm9sLCAtSEdOQ19JRCwgLWBuc3YvZXN2IChkYlZhcilgLAogICAgLUFzc2VtYmx5LCAtQ2hyb21vc29tZUFjY2Vzc2lvbiwgLVJlZmVyZW5jZUFsbGVsZSwKICAgIC1BbHRlcm5hdGVBbGxlbGUsIC1DeXRvZ2VuZXRpYwogICkgJT4lCiAgbXV0YXRlKHZhcl9sZW4gPSBwbWF4KAogICAgc3RyX2xlbmd0aChSZWZlcmVuY2VBbGxlbGVWQ0YpLAogICAgc3RyX2xlbmd0aChBbHRlcm5hdGVBbGxlbGVWQ0YpCiAgKSkgJT4lCiAgZmlsdGVyKHZhcl9sZW4gPD0gMTApICU+JQogIGFycmFuZ2UoUG9zaXRpb25WQ0YpCgojIG9ubHkga2VlcCBtYWluIHRyYW5zY3JpcHQgdmFyaWFudHMKY2xpbnZhcl9oZzM4X0VHRlIgPC0KICBjbGludmFyX2hnMzhfRUdGUiAlPiUKICBmaWx0ZXIoTmFtZSAhPSAiTk1fMDAxMzQ2OTQxLjIoRUdGUik6Yy44OS00NTM2Xzg5LTQ1MjlkZWwiKQpjbGludmFyX2hnMzhfRUdGUgpgYGAKCgoKCmBgYHtyfQpjbGludmFyX2hnMzhfRUdGUl9yZW5hbWVkIDwtCiAgY2xpbnZhcl9oZzM4X0VHRlIgJT4lCiAgZHBseXI6OnNlbGVjdCgKICAgIE5hbWUsIFN0YXJ0LCBTdG9wLAogICAgUmVmZXJlbmNlQWxsZWxlVkNGLAogICAgQWx0ZXJuYXRlQWxsZWxlVkNGLAogICAgVmFyaWF0aW9uSUQKICApICU+JQogIHNldF9uYW1lcyhjKAogICAgIm5hbWUiLCAic3RhcnQiLCAic3RvcCIsCiAgICAicmVmIiwgImFsdCIsICJjbGludmFyX2lkIgogICkpICU+JQogIG11dGF0ZShuYW1lID0gc3RyX3JlcGxhY2UoCiAgICBuYW1lLCAiTk1fMDA1MjI4LjVcXChFR0ZSXFwpOiIsCiAgICBwYXN0ZTAoImciLCBzdGFydCwgIl8iKQogICkpICU+JQogIG11dGF0ZShjbGludmFyX2lkID0gYXMuY2hhcmFjdGVyKGNsaW52YXJfaWQpKQpjbGludmFyX2hnMzhfRUdGUl9yZW5hbWVkCmBgYAoKYGBge3J9CmNsaW52YXJfaGczOF9FR0ZSX25vdF9zbnYgPC0KICBjbGludmFyX2hnMzhfRUdGUl9yZW5hbWVkICU+JQogIGZpbHRlcighKHN0cl9sZW5ndGgocmVmKSA9PSAxICYKICAgIHN0cl9sZW5ndGgoYWx0KSA9PSAxKSkgJT4lCiAgbXV0YXRlKHR5cGUgPSBjYXNlX3doZW4oCiAgICAoc3RyX2xlbmd0aChyZWYpID09IHN0cl9sZW5ndGgoYWx0KSAmCiAgICAgIHN0cl9sZW5ndGgocmVmKSA9PSAxKSB+ICJzbnYiLAogICAgKHN0cl9zdWIocmVmLCAxLCAxKSA9PSBhbHQgJgogICAgICBzdHJfbGVuZ3RoKGFsdCkgPCBzdHJfbGVuZ3RoKHJlZikpIH4gImRlbCIsCiAgICAoc3RyX3N1YihhbHQsIDEsIDEpID09IHJlZiAmCiAgICAgIHN0cl9sZW5ndGgocmVmKSA9PSAxICYKICAgICAgc3RyX2xlbmd0aChhbHQpID4gMSkgfiAiaW5zIiwKICAgIFRSVUUgfiAiaW5kZWwiCiAgKSkgJT4lCiAgbXV0YXRlKHN0b3AgPSBpZl9lbHNlKHR5cGUgPT0gImlucyIsIHN0b3AgLSAxLCBzdG9wKSkgJT4lCiAgbXV0YXRlKAogICAgYWx0ID0gaWZfZWxzZSh0eXBlID09ICJkZWwiLCAiIiwgYWx0KSwKICAgIHJlZiA9IGlmX2Vsc2UodHlwZSA9PSAiZGVsIiwgc3RyX3N1YihyZWYsIDIpLCByZWYpCiAgKSAlPiUKICByZW5hbWUoY2xpbnZhcl9pZCA9ICJpZCIpICU+JQogIHJlbG9jYXRlKHR5cGUsIC5hZnRlciA9ICJuYW1lIikKY2xpbnZhcl9oZzM4X0VHRlJfbm90X3NudgpgYGAKCmBgYHtyfQpjbGludmFyX2hnMzhfRUdGUl9zbnYgPC0KICBjbGludmFyX2hnMzhfRUdGUl9yZW5hbWVkICU+JQogIGZpbHRlcigoc3RyX2xlbmd0aChyZWYpID09IDEgJiBzdHJfbGVuZ3RoKGFsdCkgPT0gMSkpCmNsaW52YXJfaGczOF9FR0ZSX3NudgpgYGAKCiMjIExvYWQgQ09TTUlDIGRhdGEKCgoKYGBge3J9CmNvc21pY19kdCA8LQogIHJlYWRfdHN2KGNvc21pY19wYXRoKQoKY29zbWljX2R0IDwtCiAgY29zbWljX2R0ICU+JQogIGZpbHRlcihUUkFOU0NSSVBUX0FDQ0VTU0lPTiA9PSAiRU5TVDAwMDAwMjc1NDkzLjYiKSAlPiUKICBncm91cF9ieShHRU5PTUlDX01VVEFUSU9OX0lEKSAlPiUKICBkcGx5cjo6c2xpY2UoMSkgJT4lCiAgdW5ncm91cCgpCgpjb3NtaWNfZHRfc3Vic2V0IDwtCiAgY29zbWljX2R0ICU+JQogIGRwbHlyOjpzZWxlY3QoCiAgICBNVVRBVElPTl9DRFMsIE1VVEFUSU9OX0FBLAogICAgR0VOT01FX1NUQVJULCBHRU5PTUVfU1RPUCwKICAgIEdFTk9NSUNfV1RfQUxMRUxFLCBHRU5PTUlDX01VVF9BTExFTEUsCiAgICBHRU5PTUlDX01VVEFUSU9OX0lELCBNVVRBVElPTl9ERVNDUklQVElPTgogICkgJT4lCiAgbXV0YXRlKE1VVEFUSU9OX0FBID0gc3RyX3JlcGxhY2UoTVVUQVRJT05fQUEsICJwLlxcPyIsICIiKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSBpZl9lbHNlKE1VVEFUSU9OX0FBID09ICIiLAogICAgcGFzdGUwKCJnIiwgR0VOT01FX1NUQVJULCAiXyIsIE1VVEFUSU9OX0NEUyksCiAgICBwYXN0ZTAoImciLCBHRU5PTUVfU1RBUlQsICJfIiwgTVVUQVRJT05fQ0RTLCAiICgiLCBNVVRBVElPTl9BQSwgIikiKQogICkpICU+JQogIGRwbHlyOjpzZWxlY3QoCiAgICBuYW1lLCBHRU5PTUVfU1RBUlQsIEdFTk9NRV9TVE9QLAogICAgR0VOT01JQ19XVF9BTExFTEUsIEdFTk9NSUNfTVVUX0FMTEVMRSwKICAgIEdFTk9NSUNfTVVUQVRJT05fSUQKICApICU+JQogIHNldF9uYW1lcyhjKCJuYW1lIiwgInN0YXJ0IiwgInN0b3AiLCAicmVmIiwgImFsdCIsICJjb3NtaWNfaWQiKSkgJT4lCiAgbXV0YXRlKAogICAgcmVmID0gaWZfZWxzZShpcy5uYShyZWYpLCAiIiwgcmVmKSwKICAgIGFsdCA9IGlmX2Vsc2UoaXMubmEoYWx0KSwgIiIsIGFsdCkKICApICU+JQogIG11dGF0ZSh2YXJfbGVuID0gcG1heChzdHJfbGVuZ3RoKHJlZiksIHN0cl9sZW5ndGgoYWx0KSkpICU+JQogIGZpbHRlcih2YXJfbGVuIDw9IDEwKSAlPiUKICBkcGx5cjo6c2VsZWN0KC12YXJfbGVuKSAlPiUKICBhcnJhbmdlKHN0YXJ0KQoKCgpjb3NtaWNfZHRfc3Vic2V0X3NudiA8LQogIGNvc21pY19kdF9zdWJzZXQgJT4lCiAgZmlsdGVyKHN0cl9sZW5ndGgocmVmKSA9PSAxICYgc3RyX2xlbmd0aChhbHQpID09IDEpCgpjb3NtaWNfZHRfc3Vic2V0X25vdF9zbnYgPC0KICBjb3NtaWNfZHRfc3Vic2V0ICU+JQogIGZpbHRlcighKHN0cl9sZW5ndGgocmVmKSA9PSAxICYgc3RyX2xlbmd0aChhbHQpID09IDEpKQoKY29zbWljX2R0X3N1YnNldF9zbnYKYGBgCgoKYGBge3J9CmNvc21pY19kdF9zdWJzZXRfbm90X3NudiA8LQogIGNvc21pY19kdF9zdWJzZXRfbm90X3NudiAlPiUKICBtdXRhdGUodHlwZSA9IGNhc2Vfd2hlbigKICAgIChzdHJfbGVuZ3RoKHJlZikgPT0gc3RyX2xlbmd0aChhbHQpICYgc3RyX2xlbmd0aChyZWYpID09IDEpIH4gInNudiIsCiAgICAoc3RyX2xlbmd0aChyZWYpID49IDEgJiBzdHJfbGVuZ3RoKGFsdCkgPT0gMCkgfiAiZGVsIiwKICAgIChzdHJfbGVuZ3RoKHJlZikgPT0gMCAmIHN0cl9sZW5ndGgoYWx0KSA+PSAxKSB+ICJpbnMiLAogICAgVFJVRSB+ICJpbmRlbCIKICApKSAlPiUKICByZWxvY2F0ZSh0eXBlLCAuYWZ0ZXIgPSBuYW1lKQoKCmNvc21pY19kdF9zdWJzZXRfbm90X3NudiA8LQogIGNvc21pY19kdF9zdWJzZXRfbm90X3NudiAlPiUKICBtdXRhdGUoZXh0cmFfYmFzZSA9IG1hcF9jaHIoCiAgICBjb3NtaWNfZHRfc3Vic2V0X25vdF9zbnYkc3RhcnQsCiAgICB+IGFzLmNoYXJhY3RlcihzdWJzZXEoaGdfZ2Vub21lW1siY2hyNyJdXSwgLiwgLikpCiAgKSkgJT4lCiAgbXV0YXRlKAogICAgc3RvcCA9IGlmX2Vsc2UodHlwZSA9PSAiaW5zIiwgc3RvcCAtIDEsIHN0b3ApLAogICAgcmVmID0gaWZfZWxzZSh0eXBlID09ICJpbnMiLCBleHRyYV9iYXNlLCByZWYpLAogICAgYWx0ID0gaWZfZWxzZSh0eXBlID09ICJpbnMiLCBwYXN0ZTAoZXh0cmFfYmFzZSwgYWx0KSwgYWx0KQogICkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZXh0cmFfYmFzZSkKY29zbWljX2R0X3N1YnNldF9ub3Rfc252CmBgYAoKYGBge3J9CnNudl9kdCA8LQogIGZ1bGxfam9pbihjb3NtaWNfZHRfc3Vic2V0X3NudiwKICAgIGNsaW52YXJfaGczOF9FR0ZSX3NudiwKICAgIGJ5ID0gYygic3RhcnQiLCAic3RvcCIsICJyZWYiLCAiYWx0IikKICApICU+JQogIG11dGF0ZShuYW1lID0gaWZfZWxzZShpcy5uYShuYW1lLngpLCBuYW1lLnksIG5hbWUueCkpICU+JQogIGRwbHlyOjpzZWxlY3QoCiAgICBuYW1lLCBzdGFydCwgc3RvcCwKICAgIHJlZiwgYWx0LCBjb3NtaWNfaWQsCiAgICBjbGludmFyX2lkCiAgKSAlPiUKICBhcnJhbmdlKHN0YXJ0KSAlPiUKICBtdXRhdGUobmFtZSA9IGdzdWIoIl9jXFwuXFxkK1srLV0/XFxkKyhbWzphbHBoYTpdXSk+IiwgIl9cXDE+IiwgbmFtZSkpCnNudl9kdApgYGAKCgojIyBMb2FkIHZhcmlhbnRzIGZyb20gYmFzZSBlZGl0b3IgZXhwZXJpbWVudHMKCmBgYHtyfQpBQkU4ZV9hbGxfZHQgPC0gcmVhZF9jc3YoQUJFOGVfcGF0aCkKQkU0bWF4X2FsbF9kdCA8LSByZWFkX2NzdihCRTRtYXhfcGF0aCkKQUJFOGVfZHQgPC0gQUJFOGVfYWxsX2R0ICU+JQogIGRwbHlyOjpzZWxlY3QoYygxNCwgMTUsIDE3LCAyMCwgMjcpKQpCRTRtYXhfZHQgPC0gQkU0bWF4X2FsbF9kdCAlPiUKICBkcGx5cjo6c2VsZWN0KGMoMTQsIDE1LCAxNywgMjAsIDI3KSkKYmFzZV9lZGl0b3JfdmFyaWFudHMgPC0KICBiaW5kX3Jvd3MoQkU0bWF4X2R0LCBBQkU4ZV9kdCkgJT4lCiAgZHBseXI6OmZpbHRlcihNdXRhdGlvbi5jYXRlZ29yeV9zaW1wbGlmaWVkICVpbiUKICAgIGMoIk5vbnNlbnNlIiwgIk1pc3NlbnNlIiwgIlNwbGljZS1hY2NlcHRvciIsICJTcGxpY2UtZG9ub3IiKSkKYmFzZV9lZGl0b3JfdmFyaWFudHMgPC0KICBiYXNlX2VkaXRvcl92YXJpYW50cyAlPiUKICBzZXBhcmF0ZV9yb3dzKE51Y2xlb3RpZGUuZWRpdHMsIHNlcCA9ICJDXyIpICU+JQogIHNlcGFyYXRlX3Jvd3MoTnVjbGVvdGlkZS5lZGl0cywgc2VwID0gIkFfIikgJT4lCiAgc2VwYXJhdGVfcm93cyhOdWNsZW90aWRlLmVkaXRzLCBzZXAgPSAiXyIpICU+JQogIHNlcGFyYXRlX3Jvd3MoTnVjbGVvdGlkZS5lZGl0cywgc2VwID0gIjsiKSAlPiUKICBkcGx5cjo6ZmlsdGVyKE51Y2xlb3RpZGUuZWRpdHMgIT0gIiIpICU+JQogIG11dGF0ZShOdWNsZW90aWRlLmVkaXRzID0gYXMuaW50ZWdlcihOdWNsZW90aWRlLmVkaXRzKSkgJT4lCiAgbXV0YXRlKHN0YXJ0ID0gaWZfZWxzZShzZ1JOQS5TdHJhbmQgPT0gInNlbnNlIiwKICAgIHNncm5hLmdlbm9taWMucG9zaXRpb24gKyAoTnVjbGVvdGlkZS5lZGl0cyAtIDEpLAogICAgc2dybmEuZ2Vub21pYy5wb3NpdGlvbiAtIChOdWNsZW90aWRlLmVkaXRzIC0gMSkKICApKSAlPiUKICBtdXRhdGUoCiAgICBlZGl0ID0KICAgICAgY2FzZV93aGVuKAogICAgICAgIEVkaXQgPT0gIkMtVCIgJiBzZ1JOQS5TdHJhbmQgPT0gImFudGlzZW5zZSIgfiAiRy1BIiwKICAgICAgICBFZGl0ID09ICJBLUciICYgc2dSTkEuU3RyYW5kID09ICJhbnRpc2Vuc2UiIH4gIlQtQyIsCiAgICAgICAgVFJVRSB+IEVkaXQKICAgICAgKQogICkgJT4lCiAgZHBseXI6OnNlbGVjdChzdGFydCwgZWRpdCkgJT4lCiAgZ3JvdXBfYnkoc3RhcnQpICU+JQogIGRwbHlyOjpzbGljZSgxKSAlPiUKICB0aWR5cjo6c2VwYXJhdGUoZWRpdCwKICAgIGludG8gPSBjKCJyZWYiLCAiYWx0IiksIHNlcCA9ICItIgogICkgJT4lCiAgbXV0YXRlKAogICAgc3RvcCA9IHN0YXJ0LAogICAgbmFtZSA9IHBhc3RlMCgiZyIsIHN0YXJ0LCAiXyIsIHJlZiwgIj4iLCBhbHQpCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KGMoIm5hbWUiLCAic3RhcnQiLCAic3RvcCIsICJyZWYiLCAiYWx0IikpCmJhc2VfZWRpdG9yX3ZhcmlhbnRzCmBgYAoKCiMjIyBDb21iaW5lIHZhcmlhbnRzIGRhdGEKCmBgYHtyfQpzbnZfZHQgPC0KICBmdWxsX2pvaW4oc252X2R0LAogICAgYmFzZV9lZGl0b3JfdmFyaWFudHMsCiAgICBieSA9IGMoInN0YXJ0IiwgInN0b3AiLCAicmVmIiwgImFsdCIpCiAgKQoKc252X2R0IDwtCiAgc252X2R0ICU+JQogIG11dGF0ZShuYW1lID0gaWZfZWxzZShpcy5uYShuYW1lLngpLCBuYW1lLnksIG5hbWUueCkpICU+JQogIHJlbmFtZShuYW1lLnkgPSAiYmVfaWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KAogICAgbmFtZSwgc3RhcnQsIHN0b3AsIHJlZiwKICAgIGFsdCwgY29zbWljX2lkLCBjbGludmFyX2lkLCBiZV9pZAogICkgJT4lCiAgdW5pdGUoImlkIiwgY29zbWljX2lkOmJlX2lkLCBzZXAgPSAiOyIsIG5hLnJtID0gVFJVRSkgJT4lCiAgYXJyYW5nZShzdGFydCkgJT4lCiAgbXV0YXRlKHR5cGUgPSAic252IikKc252X2R0CmBgYAoKCmBgYHtyfQpub25fc252X2R0IDwtCiAgZnVsbF9qb2luKGNsaW52YXJfaGczOF9FR0ZSX25vdF9zbnYsCiAgICBjb3NtaWNfZHRfc3Vic2V0X25vdF9zbnYsCiAgICBieSA9IGMoInN0YXJ0IiwgInN0b3AiLCAicmVmIiwgImFsdCIsICJ0eXBlIikKICApICU+JQogIG11dGF0ZShuYW1lID0gaWZfZWxzZShpcy5uYShuYW1lLngpLCBuYW1lLnksIG5hbWUueCkpICU+JQogIGRwbHlyOjpzZWxlY3QoCiAgICBuYW1lLCB0eXBlLCBzdGFydCwgc3RvcCwKICAgIHJlZiwgYWx0LCBjb3NtaWNfaWQsCiAgICBpZAogICkgJT4lCiAgYXJyYW5nZShzdGFydCkgJT4lCiAgdW5pdGUoImlkIiwgY29zbWljX2lkOmlkLCBzZXAgPSAiOyIsIG5hLnJtID0gVFJVRSkKbm9uX3Nudl9kdApgYGAKCgpgYGB7cn0KdmFyaWFudHNfZHQgPC0KICBiaW5kX3Jvd3MoCiAgICBzbnZfZHQsCiAgICBub25fc252X2R0CiAgKSAlPiUKICBhcnJhbmdlKHN0YXJ0KSAlPiUKICBtdXRhdGUobmFtZSA9IHN1YigiIFxcKHBcXC4uKiQiLCAiIiwgbmFtZSkpCgp2YXJpYW50c19kdApgYGAKCgoKCiMjIEFubm90YXRlIFZhcmlhbnRzCgoKYGBge3J9CmFubm90YXRlX2FuZF9wcmVkaWN0X3ZhcmlhbnRzIDwtIGZ1bmN0aW9uKHZhcmlhbnRzX2R0KSB7CiAgc2VxX2xlbl9taW4gPC0gNDkKCiAgbnVtX3ZhcmlhbnRzIDwtIE5ST1codmFyaWFudHNfZHQpCiAgZ3IwIDwtCiAgICBHUmFuZ2VzKAogICAgICBSbGUoYygiY2hyNyIpLCBudW1fdmFyaWFudHMpLAogICAgICBJUmFuZ2VzKAogICAgICAgIHN0YXJ0ID0gdmFyaWFudHNfZHQkc3RhcnQsCiAgICAgICAgZW5kID0gdmFyaWFudHNfZHQkc3RvcCwKICAgICAgICBuYW1lcyA9IHZhcmlhbnRzX2R0JG5hbWUKICAgICAgKQogICAgKQoKICB2YXJfYWxsZWxsZXMgPC0gRE5BU3RyaW5nU2V0KHZhcmlhbnRzX2R0JGFsdCkKCiAgaW50cm9uX2xvY2F0aW9ucyA8LQogICAgbG9jYXRlVmFyaWFudHMoZ3IwLAogICAgICBpbnRieXR4LAogICAgICBJbnRyb25WYXJpYW50cygpLAogICAgICB2YXJBbGxlbGUgPSB2YXJfYWxsZWxsZXMKICAgICkKICAjIExvY2F0ZSBpbnRyb25pYyB2YXJpYW50cwogIGludHJvbl9sb2NhdGlvbnMgPC0KICAgIGludHJvbl9sb2NhdGlvbnMgJT4lCiAgICBhc190aWJibGUoKSAlPiUKICAgIGFkZF9jb2x1bW4obmFtZSA9IG5hbWVzKGludHJvbl9sb2NhdGlvbnMpKSAlPiUKICAgIHJlbG9jYXRlKG5hbWUsIC5iZWZvcmUgPSAic2VxbmFtZXMiKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLVBSRUNFREVJRCwgLUZPTExPV0lELCAtVFhJRCkKCiAgIyBMb2NhdGUgcmVtYWluaW5nIHZhcmlhbnRzCiAgYWxsIDwtIGxvY2F0ZVZhcmlhbnRzKGdyMCwKICAgIHR4ZGIsCiAgICBBbGxWYXJpYW50cygpLAogICAgdmFyQWxsZWxlID0gdmFyX2FsbGVsbGVzCiAgKQogIGFsbCA8LQogICAgYWxsICU+JQogICAgYXNfdGliYmxlKCkgJT4lCiAgICBhZGRfY29sdW1uKG5hbWUgPSBuYW1lcyhhbGwpKSAlPiUKICAgIHJlbG9jYXRlKG5hbWUsIC5iZWZvcmUgPSAic2VxbmFtZXMiKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLVBSRUNFREVJRCwgLUZPTExPV0lEKQoKICAjIEZsYWcgaW50cm9uaWMgYW5kIDVVVFIgdmFyaWFudHMgdGhhdCBoYXZlIGFsdGVybmF0aXZlIAogICMgZWZmZWN0cyBnaXZlbiBvdGhlciBwb3RlbnRpYWwgc3BsaWNlZCB0cmFuc2NyaXB0cwoKICB0cmFuc2NyaXB0X3NwbGljZV9zaXRlcyA8LQogICAgYWxsICU+JQogICAgZHBseXI6OmZpbHRlcihMT0NBVElPTiA9PSAic3BsaWNlU2l0ZSIgJiBUWElEID09ICI5MzUwMiIpCgogIGludHJvbl9sb2NhdGlvbnMgPC0KICAgIGludHJvbl9sb2NhdGlvbnMgJT4lCiAgICBmaWx0ZXIoIShuYW1lICVpbiUgdHJhbnNjcmlwdF9zcGxpY2Vfc2l0ZXMkbmFtZSkpCgogIHVuY2VydGFpbl9pbnRyb25zIDwtCiAgICBhbGwgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKG5hbWUgJWluJSBpbnRyb25fbG9jYXRpb25zJG5hbWUpICU+JQogICAgZ3JvdXBfYnkobmFtZSwgTE9DQVRJT04pICU+JQogICAgZHBseXI6OnNsaWNlKDEpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZ3JvdXBfYnkobmFtZSkgJT4lCiAgICBtdXRhdGUoZnJhY19pbnRyb24gPSBzdW0oTE9DQVRJT04gPT0gImludHJvbiIpIC8gbigpKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoZnJhY19pbnRyb24gPCAxKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoTE9DQVRJT04gIT0gImludHJvbiIpICU+JQogICAgZHBseXI6OmZpbHRlcihUWElEICE9IDkzNTAyKSAlPiUKICAgIGFycmFuZ2UoUVVFUllJRCkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1HRU5FSUQsIC1mcmFjX2ludHJvbikgJT4lCiAgICB1bmdyb3VwKCkKCiAgZmxhZ192YXJpYW50cyA8LQogICAgdW5jZXJ0YWluX2ludHJvbnMgJT4lCiAgICBkcGx5cjo6c2VsZWN0KG5hbWUsIExPQ0FUSU9OLCBUWElEKSAlPiUKICAgIGxlZnRfam9pbiguLCB0eF9sZW5ndGhzICU+JQogICAgICBkcGx5cjo6c2VsZWN0KHR4X2lkLCB0eF9uYW1lKSAlPiUKICAgICAgbXV0YXRlKHR4X2lkID0gYXMuY2hhcmFjdGVyKHR4X2lkKSkgJT4lCiAgICAgIGRwbHlyOjpyZW5hbWUoVFhJRCA9ICJ0eF9pZCIpLAogICAgYnkgPSAiVFhJRCIKICAgICkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1UWElEKSAlPiUKICAgIHVuaXRlKCJhbHRfZWZmZWN0IiwgTE9DQVRJT046dHhfbmFtZSkKCgogIGFsbCA8LQogICAgYWxsICU+JQogICAgZHBseXI6OmZpbHRlcihUWElEID09IDkzNTAyKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLVRYSUQpCgogIHBvdGVudGlhbF9wcm9tb3RlcnMgPC0KICAgIGFsbFt3aGljaChkdXBsaWNhdGVkKGFsbCRuYW1lKSksIF0gJT4lCiAgICBkcGx5cjo6c2VsZWN0KG5hbWUpICU+JQogICAgbXV0YXRlKGFsdF9lZmZlY3QgPSAicHJvbW90ZXJfRU5TVDAwMDAwMjc1NDkzLjciKQoKICBmbGFnX3ZhcmlhbnRzIDwtCiAgICBiaW5kX3Jvd3MoZmxhZ192YXJpYW50cywgcG90ZW50aWFsX3Byb21vdGVycykKCiAgZmxhZ192YXJpYW50cyA8LQogICAgZmxhZ192YXJpYW50cyAlPiUKICAgIGdyb3VwX2J5KG5hbWUpICU+JQogICAgZHBseXI6OnNsaWNlKDEpICU+JQogICAgdW5ncm91cCgpCgoKICBhbGwgPC0KICAgIGFsbCAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoIWR1cGxpY2F0ZWQoYWxsJG5hbWUpKQoKCiAgYWxsIDwtCiAgICBiaW5kX3Jvd3MoCiAgICAgIGFsbCwKICAgICAgaW50cm9uX2xvY2F0aW9ucwogICAgKQoKICBhbGwgPC0KICAgIGxlZnRfam9pbihhbGwsIGZsYWdfdmFyaWFudHMsIGJ5ID0gIm5hbWUiKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLUdFTkVJRCkKCgogIGFsbCA8LQogICAgYWxsICU+JQogICAgZHBseXI6OnNlbGVjdCgtYyhMT0NTVEFSVDpDRFNJRCkpCgogIGdlbmVfbW9kZWxfZ3JfbGlzdCA8LQogICAgYXMoZ2VuZV9tb2RlbF9nciwgIkdSYW5nZXNMaXN0IikKCiAgbmFtZXMoZ2VuZV9tb2RlbF9ncl9saXN0KSA8LSBtY29scyhnZW5lX21vZGVsX2dyKSRpZAoKICBtYXBfZ3JzIDwtCiAgICBtYXBUb1RyYW5zY3JpcHRzKHJlc2l6ZShncjAsIDEpLCBnZW5lX21vZGVsX2dyX2xpc3QpCgogIG1hcF9kdCA8LQogICAgbWFwX2dycyAlPiUKICAgIGFzX3RpYmJsZSgpICU+JQogICAgbXV0YXRlKG5hbWUgPSBuYW1lcyhtYXBfZ3JzKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KG5hbWUsIHNlcW5hbWVzLCBzdGFydCkgJT4lCiAgICBzZXRfbmFtZXMoYygibmFtZSIsICJsb2MiLCAibG9jX3N0YXJ0IikpICU+JQogICAgbGVmdF9qb2luKC4sIGxvY19sZW5zLCBieSA9ICJsb2MiKQoKICBhbGwgPC0KICAgIGxlZnRfam9pbihhbGwsCiAgICAgIG1hcF9kdCwKICAgICAgYnkgPSAibmFtZSIKICAgICkKCiAgIyBQcmVkaWN0IGNvbnNlcXVlbmNlIG9mIHZhcmlhbnQKICBwcmVkaWN0X2NvZGluZ192YXJpYW50cyA8LQogICAgcHJlZGljdENvZGluZyhncjAsIHR4ZGIsIGhnX2dlbm9tZSwgdmFyQWxsZWxlID0gdmFyX2FsbGVsbGVzKQogIHByZWRpY3RfY29kaW5nX3ZhcmlhbnRzIDwtCiAgICBwcmVkaWN0X2NvZGluZ192YXJpYW50c1ttY29scyhwcmVkaWN0X2NvZGluZ192YXJpYW50cykkVFhJRCA9PSA5MzUwMl0KICBwcmVkaWN0X2NvZGluZ192YXJpYW50cyA8LQogICAgYXNfdGliYmxlKHByZWRpY3RfY29kaW5nX3ZhcmlhbnRzKSAlPiUKICAgIGFkZF9jb2x1bW4obmFtZSA9IG5hbWVzKHByZWRpY3RfY29kaW5nX3ZhcmlhbnRzKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KAogICAgICBuYW1lLCBDRFNMT0Muc3RhcnQsIENPTlNFUVVFTkNFLCBSRUZDT0RPTiwKICAgICAgVkFSQ09ET04sIFJFRkFBLCBWQVJBQSwgUFJPVEVJTkxPQwogICAgKQoKICBuX2Rpc3RpbmN0KGFsbCRuYW1lKQoKICBhbGwgPC0KICAgIGxlZnRfam9pbihhbGwsIHByZWRpY3RfY29kaW5nX3ZhcmlhbnRzLCBieSA9ICJuYW1lIikKCgoKICBhbGwgPC0KICAgIGFsbCAlPiUKICAgIG11dGF0ZShsb2NfZW5kX2NvbXAgPSBsb2NfbGVuIC0gKGxvY19zdGFydCArIHdpZHRoIC0gMSkpICU+JQogICAgbXV0YXRlKGJvdW5kYXJ5X2ZsYWcgPSAoTE9DQVRJT04gPT0gImNvZGluZyIgJgogICAgICAobG9jX3N0YXJ0IDwgc2VxX2xlbl9taW4gfCBsb2NfZW5kX2NvbXAgPCBzZXFfbGVuX21pbikpKQoKICBhbGwgPC0KICAgIGFsbCAlPiUKICAgIGRwbHlyOjpzZWxlY3QoCiAgICAgIG5hbWUsIENPTlNFUVVFTkNFLCBMT0NBVElPTiwgbG9jLCBsb2NfbGVuLCBsb2Nfc3RhcnQsCiAgICAgIENEU0xPQy5zdGFydCwgUFJPVEVJTkxPQywgUkVGQ09ET04sIFZBUkNPRE9OLAogICAgICBSRUZBQSwgVkFSQUEsIGJvdW5kYXJ5X2ZsYWcsIGFsdF9lZmZlY3QKICAgICkgJT4lCiAgICBzZXRfbmFtZXMoYygKICAgICAgIm5hbWUiLCAiY29uc2VxdWVuY2UiLCAibG9jYXRpb24iLCAicmVnaW9uIiwgInJlZ2lvbl9sZW4iLAogICAgICAicG9zX3JlZ2lvbiIsICJwb3NfY2RzIiwgInBvc19wcm90ZWluIiwgInJlZl9jb2RvbiIsCiAgICAgICJ2YXJfY29kb24iLCAicmVmX2FhIiwgInZhcl9hYSIsICJib3VuZGFyeV9mbGFnIiwgImFsdF9lZmZlY3QiCiAgICApKSAlPiUKICAgIHJvd3dpc2UoKSAlPiUKICAgIG11dGF0ZShwb3NfcHJvdGVpbiA9IHN0cl9jKHBvc19wcm90ZWluLCBjb2xsYXBzZSA9ICI6IikpCgogIHJldHVybihhbGwpCn0KYGBgCgoKYGBge3J9CmFsbCA8LQogIGFubm90YXRlX2FuZF9wcmVkaWN0X3ZhcmlhbnRzKHZhcmlhbnRzX2R0KQpgYGAKCgojIyBJbmNsdWRlIGJhc2UgZWRpdGluZyB2YXJpYW50cyBnZW5lcmF0ZWQgYnkgdHdvIG9yIG1vcmUgZWRpdHMKCmBgYHtyfQptaW5fYmFzZV9jaGFuZ2VzIDwtIGZ1bmN0aW9uKHRhcmdldF9hbWlub19hY2lkLCByZWZfY29kb24pIHsKICAjIERlZmluZSBhIG1hcHBpbmcgb2YgYW1pbm8gYWNpZHMgdG8gY29kb25zCiAgYW1pbm9fYWNpZF9tYXBwaW5nIDwtIGxpc3QoCiAgICAiQSIgPSBjKCJHQ1QiLCAiR0NDIiwgIkdDQSIsICJHQ0ciKSwKICAgICJSIiA9IGMoIkNHVCIsICJDR0MiLCAiQ0dBIiwgIkNHRyIsICJBR0EiLCAiQUdHIiksCiAgICAiTiIgPSBjKCJBQVQiLCAiQUFDIiksCiAgICAiRCIgPSBjKCJHQVQiLCAiR0FDIiksCiAgICAiQyIgPSBjKCJUR1QiLCAiVEdDIiksCiAgICAiUSIgPSBjKCJDQUEiLCAiQ0FHIiksCiAgICAiRSIgPSBjKCJHQUEiLCAiR0FHIiksCiAgICAiRyIgPSBjKCJHR1QiLCAiR0dDIiwgIkdHQSIsICJHR0ciKSwKICAgICJIIiA9IGMoIkNBVCIsICJDQUMiKSwKICAgICJJIiA9IGMoIkFUQSIpLCAjICJBVFQiLCAiQVRDIiwgdHdvIEE+VHMKICAgICJMIiA9IGMoIlRUQSIsICJUVEciLCAiQ1RUIiwgIkNUQyIsICJDVEEiLCAiQ1RHIiksCiAgICAiSyIgPSBjKCJBQUEiLCAiQUFHIiksCiAgICAiTSIgPSBjKCJBVEciKSwKICAgICJGIiA9IGMoIlRUVCIsICJUVEMiKSwKICAgICJQIiA9IGMoIkNDVCIsICJDQ0MiLCAiQ0NBIiwgIkNDRyIpLAogICAgIlMiID0gYygiVENUIiwgIlRDQyIsICJUQ0EiLCAiVENHIiwgIkFHVCIsICJBR0MiKSwKICAgICJUIiA9IGMoIkFDVCIsICJBQ0MiLCAiQUNBIiwgIkFDRyIpLAogICAgIlciID0gYygiVEdHIiksCiAgICAiWSIgPSBjKCJUQVQiLCAiVEFDIiksCiAgICAiViIgPSBjKCJHVFQiLCAiR1RDIiwgIkdUQSIsICJHVEciKSwKICAgICIqIiA9IGMoIlRBQSIsICJUQUciLCAiVEdBIikKICApCgogICMgRmluZCB0aGUgcmVmZXJlbmNlIGFtaW5vIGFjaWQgY29ycmVzcG9uZGluZyB0byB0aGUgcmVmZXJlbmNlIGNvZG9uCiAgcmVmX2FtaW5vX2FjaWQgPC0KICAgIG5hbWVzKGFtaW5vX2FjaWRfbWFwcGluZylbd2hpY2gocmVmX2NvZG9uICVpbiUgdW5saXN0KGFtaW5vX2FjaWRfbWFwcGluZykpXQoKICAjIENoZWNrIGlmIHRoZSByZWZlcmVuY2UgYW1pbm8gYWNpZCBpcyB2YWxpZAogIGlmIChpcy5udWxsKHJlZl9hbWlub19hY2lkKSkgewogICAgc3RvcCgiSW52YWxpZCByZWZlcmVuY2UgY29kb24uIikKICB9CgogICMgQ2hlY2sgaWYgdGhlIHRhcmdldCBhbWlubyBhY2lkIGlzIHZhbGlkCiAgaWYgKCEodGFyZ2V0X2FtaW5vX2FjaWQgJWluJSBuYW1lcyhhbWlub19hY2lkX21hcHBpbmcpKSkgewogICAgc3RvcCgiSW52YWxpZCB0YXJnZXQgYW1pbm8gYWNpZC4iKQogIH0KCiAgIyBGaW5kIHRoZSB0YXJnZXQgY29kb25zIGZvciB0aGUgZ2l2ZW4gYW1pbm8gYWNpZAogIHRhcmdldF9jb2RvbnMgPC0gYW1pbm9fYWNpZF9tYXBwaW5nW1t0YXJnZXRfYW1pbm9fYWNpZF1dCgogICMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgYmFzZSBjaGFuZ2VzIG5lZWRlZCBmb3IgZWFjaCB0YXJnZXQgY29kb24KICBiYXNlX2NoYW5nZXMgPC0KICAgIHNhcHBseSgKICAgICAgdGFyZ2V0X2NvZG9ucywKICAgICAgZnVuY3Rpb24odGMpIHN1bShzdHJzcGxpdChyZWZfY29kb24sICIiKVtbMV1dICE9IHN0cnNwbGl0KHRjLCAiIilbWzFdXSkKICAgICkKCiAgIyBGaW5kIHRoZSBpbmRleCBvZiB0aGUgbWluaW11bSBiYXNlIGNoYW5nZXMKICBtaW5faW5kZXggPC0KICAgIHdoaWNoLm1pbihiYXNlX2NoYW5nZXMpCgogICMgUmV0dXJuIHRoZSB2YXJpYW50IGNvZG9uIHdpdGggbWluaW11bSBiYXNlIGNoYW5nZXMKICByZXR1cm4odGFyZ2V0X2NvZG9uc1ttaW5faW5kZXhdKQp9CgpnZXRfY29kb25fYXRfYWFfcG9zaXRpb24gPC0gZnVuY3Rpb24ocG9zKSB7CiAgc3RhcnRfcG9zIDwtIHBvcyAqIDMgLSAyCiAgZW5kX3BvcyA8LSBwb3MgKiAzCiAgYXMuY2hhcmFjdGVyKHN1YnNlcShFR0ZSX3NlcSwgc3RhcnRfcG9zLCBlbmRfcG9zKSkKfQoKCkFCRThlX2FhX3ZhciA8LQogIEFCRThlX2FsbF9kdCAlPiUKICBkcGx5cjo6c2VsZWN0KEFtaW5vLmFjaWQuZWRpdHMpICU+JQogIHNlcGFyYXRlX3Jvd3MoQW1pbm8uYWNpZC5lZGl0cywgc2VwID0gIjsiKSAlPiUKICBkcGx5cjo6ZmlsdGVyKCEoQW1pbm8uYWNpZC5lZGl0cyAlaW4lIGMoInV0ciIsICJOb25lIiwgIiIpKSkgJT4lCiAgZHBseXI6OmZpbHRlcighc3RyX2RldGVjdChBbWluby5hY2lkLmVkaXRzLCAiRXhvbiIpKSAlPiUKICBzZXBhcmF0ZV93aWRlcl9yZWdleChBbWluby5hY2lkLmVkaXRzLAogICAgYygKICAgICAgcmVmX2FhID0gIl5bQS1aYS16XSsiLAogICAgICBwb3NfcHJvdGVpbiA9ICJbMC05XSsiLAogICAgICB2YXJfYWEgPSAiW0EtWmEtel0rJCIKICAgICksCiAgICBjb2xzX3JlbW92ZSA9IEYKICApICU+JQogIGRwbHlyOjpmaWx0ZXIocmVmX2FhICE9IHZhcl9hYSkKCgpCRTRtYXhfYWFfdmFyIDwtCiAgQkU0bWF4X2FsbF9kdCAlPiUKICBkcGx5cjo6c2VsZWN0KEFtaW5vLmFjaWQuZWRpdHMpICU+JQogIHNlcGFyYXRlX3Jvd3MoQW1pbm8uYWNpZC5lZGl0cywgc2VwID0gIjsiKSAlPiUKICBkcGx5cjo6ZmlsdGVyKCEoQW1pbm8uYWNpZC5lZGl0cyAlaW4lIGMoInV0ciIsICJOb25lIiwgIiIpKSkgJT4lCiAgZHBseXI6OmZpbHRlcighc3RyX2RldGVjdChBbWluby5hY2lkLmVkaXRzLCAiRXhvbiIpKSAlPiUKICBzZXBhcmF0ZV93aWRlcl9yZWdleChBbWluby5hY2lkLmVkaXRzLAogICAgYygKICAgICAgcmVmX2FhID0gIl5bQS1aYS16XSsiLAogICAgICBwb3NfcHJvdGVpbiA9ICJbMC05XSsiLAogICAgICB2YXJfYWEgPSAiW0EtWmEtel0rJCIKICAgICksCiAgICBjb2xzX3JlbW92ZSA9IEYKICApICU+JQogIGRwbHlyOjpmaWx0ZXIocmVmX2FhICE9IHZhcl9hYSkKCmFhX21hcCA8LSBjKEFNSU5PX0FDSURfQ09ERSwgIioiID0gIlRlciIpCmZsaXBwZWRfYWFfbWFwIDwtIHNldE5hbWVzKG5hbWVzKGFhX21hcCksIGFhX21hcCkKCmJlX3Zhcl9kdCA8LQogIGJpbmRfcm93cyhBQkU4ZV9hYV92YXIsIEJFNG1heF9hYV92YXIpICU+JQogIGdyb3VwX2J5KEFtaW5vLmFjaWQuZWRpdHMpICU+JQogIGRwbHlyOjpzbGljZSgxKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKAogICAgcmVmX2FhID0gZmxpcHBlZF9hYV9tYXBbcmVmX2FhXSwKICAgIHZhcl9hYSA9IGZsaXBwZWRfYWFfbWFwW3Zhcl9hYV0KICApCgp2YXJpYW50c19kdDAgPC0KICBsZWZ0X2pvaW4odmFyaWFudHNfZHQsIGFsbCwgYnkgPSAibmFtZSIpCgpiZV92YXJfZHQgPC0KICBsZWZ0X2pvaW4oYmVfdmFyX2R0LAogICAgdmFyaWFudHNfZHQwICU+JQogICAgICBmaWx0ZXIocG9zX3Byb3RlaW4gIT0gIiIgJiB0eXBlID09ICJzbnYiKSAlPiUKICAgICAgZHBseXI6OnNlbGVjdChuYW1lLCByZWZfYWEsIHBvc19wcm90ZWluLCB2YXJfYWEpLAogICAgYnkgPSBjKCJyZWZfYWEiLCAicG9zX3Byb3RlaW4iLCAidmFyX2FhIikKICApICU+JQogIGRwbHlyOjpmaWx0ZXIoaXMubmEobmFtZSkpICU+JQogIG11dGF0ZShwb3NfcHJvdGVpbiA9IGFzLm51bWVyaWMocG9zX3Byb3RlaW4pKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKHJlZl9jb2RvbiA9IGdldF9jb2Rvbl9hdF9hYV9wb3NpdGlvbihwb3NfcHJvdGVpbikpCgpiZV92YXJfZHQgPC0KICBiZV92YXJfZHQgJT4lCiAgbXV0YXRlKHZhcl9jb2RvbiA9IG1pbl9iYXNlX2NoYW5nZXModmFyX2FhLCByZWZfY29kb24pKQoKYmVfdmFyX2R0IDwtCiAgYmVfdmFyX2R0ICU+JQogIG11dGF0ZSgKICAgIGxhc3RfYmFzZV9kaWZmID0KICAgICAgc3RyX3N1YihyZWZfY29kb24sIDMsIDMpICE9IHN0cl9zdWIodmFyX2NvZG9uLCAzLCAzKQogICkgJT4lCiAgbXV0YXRlKAogICAgcmVmID0gaWZfZWxzZShsYXN0X2Jhc2VfZGlmZiwgcmVmX2NvZG9uLCBzdHJfc3ViKHJlZl9jb2RvbiwgMSwgMikpLAogICAgYWx0ID0gaWZfZWxzZShsYXN0X2Jhc2VfZGlmZiwgdmFyX2NvZG9uLCBzdHJfc3ViKHZhcl9jb2RvbiwgMSwgMikpLAogICAgc3RhcnQgPSByZWZfbG9jc1twb3NfcHJvdGVpbiAqIDMgLSAyXSwKICAgIHN0b3AgPSBpZl9lbHNlKGxhc3RfYmFzZV9kaWZmLCBzdGFydCArIDIsIHN0YXJ0ICsgMSkKICApICU+JQogIG11dGF0ZShuYW1lID0gcGFzdGUwKCJnIiwgc3RhcnQsICJfIiwgcmVmLCAiPiIsIGFsdCkpICU+JQogIG11dGF0ZSgKICAgIGlkID0gbmFtZSwKICAgIHR5cGUgPSAiaW5kZWwiCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KGMoIm5hbWUiLCAidHlwZSIsICJzdGFydCIsICJzdG9wIiwgInJlZiIsICJhbHQiLCAiaWQiKSkKcm0odmFyaWFudHNfZHQwKQoKIyByZW1vdmUgZzU1MTc0Nzc2X1RUPkNDIHNpbmNlIGl0IGlzIHRoZSBzYW1lIGFzIGc1NTE3NDc3Nl9jLjIyMzlfMjI0MGRlbGluc0NDCmJlX3Zhcl9kdCA8LQogIGJlX3Zhcl9kdCAlPiUKICBmaWx0ZXIobmFtZSAhPSAiZzU1MTc0Nzc2X1RUPkNDIikKYmVfdmFyX2R0CmBgYAoKCgoKIyMjIEluY2x1ZGUgZG91YmxlIGJhc2UgZWRpdHMgdmFyaWFudHMKCmBgYHtyfQp2YXJpYW50c19kdCA8LQogIGJpbmRfcm93cyh2YXJpYW50c19kdCwgYmVfdmFyX2R0KSAlPiUKICBhcnJhbmdlKHN0YXJ0KQpgYGAKCgojIyMgUmVhbm5vdGF0ZSBhZnRlciBpbmNsdXNpb24gb2YgZG91YmxlIGJhc2UgZWRpdHMgdmFyaWFudHMKCmBgYHtyfQphbGwgPC0KICBhbm5vdGF0ZV9hbmRfcHJlZGljdF92YXJpYW50cyh2YXJpYW50c19kdCkKCnZhcmlhbnRzX2R0IDwtCiAgbGVmdF9qb2luKHZhcmlhbnRzX2R0LCBhbGwsIGJ5ID0gIm5hbWUiKQoKdmFyaWFudHNfZHQKYGBgCmBgYHtyfQp0YWJsZSh2YXJpYW50c19kdCRsb2NhdGlvbikKYGBgCgpgYGB7cn0KdGFibGUodmFyaWFudHNfZHQkbG9jYXRpb24sIHZhcmlhbnRzX2R0JHR5cGUpCmBgYAoKCiMjIyBGaWx0ZXIgb3V0IGludHJvbnMKCgpgYGB7cn0KdmFyaWFudHNfZHQgPC0KICB2YXJpYW50c19kdCAlPiUKICBmaWx0ZXIobG9jYXRpb24gIT0gYygiaW50cm9uIikpCnZhcmlhbnRzX2R0CmBgYAoKCgojIyBHZW5lcmF0ZSBzeW5vbnltb3VzIG11dGF0aW9ucyBuZWFyIHRhcmdldCBTaW5nbGUgTnVjbGVvdGlkZSBWYXJpYW50cwoKCmBgYHtyfQojIEZ1bmN0aW9uIHRvIHNwbGl0IGEgc3RyaW5nIGludG8gY29uc2VjdXRpdmUgdHJpcGxldHMKc3BsaXRfaW50b190cmlwbGV0cyA8LSBmdW5jdGlvbihzKSB7CiAgbiA8LSBuY2hhcihzKQogIGlmIChuICUlIDMgIT0gMCkgewogICAgd2FybmluZygiU3RyaW5nIGxlbmd0aCBpcyBub3QgYSBtdWx0aXBsZSBvZiAzLiBQYWRkaW5nIHdpdGggc3BhY2VzLiIpCiAgICBzIDwtIHBhc3RlMChzLCByZXAoIiAiLCAzIC0gKG4gJSUgMykpKQogIH0KICBtYXRyaXgoc3Ryc3BsaXQocywgIiIpW1sxXV0sIG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpCn0KCiMgU3BsaXQgdGhlIHZlY3RvciBpbnRvIHRyaXBsZXRzCnRyaXBsZXRzIDwtIHNwbGl0X2ludG9fdHJpcGxldHMoYXMuY2hhcmFjdGVyKEVHRlJfc2VxKSkKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lCmNvZG9uX21hcF9kZiA8LQogIHRpYmJsZShyZWZfY29kb24gPSBhcHBseSh0cmlwbGV0cywgMSwgcGFzdGUsIGNvbGxhcHNlID0gIiIpKSAlPiUKICBtdXRhdGUocG9zX3Byb3RlaW4gPSByb3dfbnVtYmVyKCkpCmNvZG9uX21hcF9kZgpgYGAKCgpgYGB7cn0Kc3luX2NvZG9uX21hcCA8LQogIHJlYWRfZGVsaW0oc3luX2NvZG9uX3BhdGgpCnN5bl9jb2Rvbl9tYXAKYGBgCgpgYGB7cn0KY29kb25fbWFwX2RmIDwtCiAgbGVmdF9qb2luKGNvZG9uX21hcF9kZiwgc3luX2NvZG9uX21hcCwgYnkgPSAicmVmX2NvZG9uIikgJT4lCiAgbXV0YXRlKHN0YXJ0ID0gcmVmX2xvY3NbcG9zX3Byb3RlaW4gKiAzXSkgJT4lCiAgZmlsdGVyKCFpcy5uYShyZWZfYWEpKQpjb2Rvbl9tYXBfZGYKYGBgCgoKCiMjIyBTcGxpdCBTTlZTIGludG8gc3BsaWNlU2l0ZSBjb2RpbmcsIGFuZCBpbnRyb24gdmFyaWFudHMKCmBgYHtyfQp2YXJpYW50c19zbnYgPC0KICB2YXJpYW50c19kdCAlPiUKICBmaWx0ZXIodHlwZSA9PSAic252IikKCnZhcmlhbnRzX3Nudl9jb2RpbmcgPC0KICB2YXJpYW50c19zbnYgJT4lCiAgZmlsdGVyKGxvY2F0aW9uICVpbiUgYygiY29kaW5nIikpCmBgYAoKIyMjIEZpbmQgbm9uc3lub21vdXMgbXV0YXRpb25zCgpgYGB7cn0KZ2V0X2NvZG9uX21hcCA8LSBmdW5jdGlvbihzdGFydCwgbmFtZSwgbnVtX2lkeCkgewogIGRpcyA8LSBhYnMoc3RhcnQgLSBjb2Rvbl9tYXBfZGYkc3RhcnQpCiAgZGlzW2RpcyA8PSAyXSA8LSAxMDAwMAogIGxvd2VzdF9pbmRpY2VzIDwtIGhlYWQob3JkZXIoZGlzKSwgbnVtX2lkeCkKCiAgYnBfZGlzdCA8LSBjb2Rvbl9tYXBfZGYkc3RhcnRbbG93ZXN0X2luZGljZXNdIC0gc3RhcnQKCiAgZHQgPC0gdGliYmxlKG5hbWUgPSBuYW1lLCBkaXN0ID0gYnBfZGlzdCkKICBkdCA8LSBiaW5kX2NvbHMoZHQsIGNvZG9uX21hcF9kZltsb3dlc3RfaW5kaWNlcywgXSkKICByZXR1cm4oZHQpCn0KCmNvZGluZ19zeW4gPC0KICBtYXAyX2RmKHZhcmlhbnRzX3Nudl9jb2Rpbmckc3RhcnQsCiAgICB2YXJpYW50c19zbnZfY29kaW5nJG5hbWUsCiAgICBnZXRfY29kb25fbWFwLAogICAgbnVtX2lkeCA9IDEKICApICU+JQogIHJlbmFtZShuYW1lID0gInRhcmdldF9uYW1lIikgJT4lCiAgbXV0YXRlKG5hbWUgPSBwYXN0ZTAoImciLCBzdGFydCwgIl8iLCByZWYsICI+IiwgYWx0KSkgJT4lCiAgZHBseXI6OnNlbGVjdCgKICAgIG5hbWUsIHN0YXJ0LCByZWYsIGFsdCwgcmVmLAogICAgdGFyZ2V0X25hbWUsIHBvc19wcm90ZWluLAogICAgcmVmX2NvZG9uLCB2YXJfY29kb24sIHJlZl9hYSwgZGlzdAogICkKYGBgCgpgYGB7cn0KdGFibGUoY29kaW5nX3N5biRkaXN0KQpgYGAKCmBgYHtyfQpjb2Rpbmdfc3luIDwtCiAgY29kaW5nX3N5biAlPiUKICBkcGx5cjo6c2VsZWN0KC1kaXN0KSAlPiUKICBncm91cF9ieShuYW1lKSAlPiUKICBtdXRhdGUodGFyZ2V0X25hbWUgPSBwYXN0ZSh0YXJnZXRfbmFtZSwgY29sbGFwc2UgPSAiOyIpKSAlPiUKICBkcGx5cjo6c2xpY2UoMSkKY29kaW5nX3N5bgpgYGAKCgpgYGB7cn0KIyMgUHJldmlvdXMgY29kZSBmb3IgaW5jbHVkaW5nIGludHJvbmljIHZhcmlhbnRzISEKCiMgdmFyaWFudHNfc252X2ludHJvbiA8LQojICAgdmFyaWFudHNfc252ICU+JQojICAgZmlsdGVyKGxvY2F0aW9uICVpbiUgYygiaW50cm9uIiwgInNwbGljZVNpdGUiKSkKCiMgbXV0X3Bvc19kaXN0IDwtIDMKIyBpbnRyb25fc3luIDwtCiMgICB2YXJpYW50c19zbnZfaW50cm9uICU+JQojICAgZHBseXI6OnNlbGVjdChuYW1lLCBzdGFydCwgcmVnaW9uX2xlbiwgcG9zX3JlZ2lvbikgJT4lCiMgICBtdXRhdGUocG9zX3JlZ2lvbl9jb21wID0gcmVnaW9uX2xlbiAtIHBvc19yZWdpb24pICU+JQojICAgbXV0YXRlKHVwc3RyZWFtID0gY2FzZV93aGVuKAojICAgICBwb3NfcmVnaW9uX2NvbXAgPCA4IH4gRiwKIyAgICAgcG9zX3JlZ2lvbl9jb21wID49IDggfiBUCiMgICApKSAlPiUKIyAgIG11dGF0ZSgKIyAgICAgc3RhcnRfc3luID0gaWZfZWxzZSh1cHN0cmVhbSwgc3RhcnQgKyBtdXRfcG9zX2Rpc3QsIHN0YXJ0IC0gbXV0X3Bvc19kaXN0KSwKIyAgICAgcmVmID0gbWFwX2NocigKIyAgICAgICBzdGFydF9zeW4sCiMgICAgICAgfiBhcy5jaGFyYWN0ZXIoc3Vic2VxKGhnX2dlbm9tZVtbImNocjciXV0sIC4sIC4pKQojICAgICApCiMgICApCiMKCiMgZHVwX2FsdGVybmF0aXZlcyA8LQojICAgaW50cm9uX3N5biAlPiUKIyAgIGZpbHRlcihzdGFydCAlaW4lIGludHJvbl9zeW4kc3RhcnRfc3luKSAlPiUKIyAgIHNlcGFyYXRlKG5hbWUsIGludG8gPSBjKCJlZGl0IiwgImFsdDAiKSwgcmVtb3ZlID0gRiwgc2VwID0gIj4iKSAlPiUKIyAgIHNlcGFyYXRlKGVkaXQsIGludG8gPSBjKCJsb2MiLCAicmVmMCIpLCByZW1vdmUgPSBGLCBzZXAgPSAiXyIpICU+JQojICAgZHBseXI6OnNlbGVjdChuYW1lLCBzdGFydCwgcmVmMCwgYWx0MCkgJT4lCiMgICBncm91cF9ieShzdGFydCkgJT4lCiMgICBtdXRhdGUoYmFzZXMgPSBwYXN0ZShhbHQwLCBjb2xsYXBzZSA9ICIsIikpICU+JQojICAgdW5ncm91cCgpICU+JQojICAgbXV0YXRlKGJhc2VzID0gcGFzdGUwKGJhc2VzLCAiLCIsIHJlZjApKSAlPiUKIyAgIHJvd3dpc2UoKSAlPiUKIyAgIG11dGF0ZShhbHRfMiA9IHNldGRpZmYoYygiQSIsICJDIiwgIlQiLCAiRyIpLAojICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfc3BsaXQoYmFzZXMsIHBhdHRlcm4gPSAiLCIpW1sxXV0pWzFdKSAlPiUKIyAgIG11dGF0ZShuYW1lX3N5bSA9IHBhc3RlMCgiZyIsIHN0YXJ0LCAiXyIsIHJlZjAsICI+IiwgYWx0XzIpKSAlPiUKIyAgIGRwbHlyOjpzZWxlY3QobmFtZSwgbmFtZV9zeW0sIGFsdF8yKQojIGR1cF9hbHRlcm5hdGl2ZXMKCiMgaW50cm9uX3N5biA8LQojICAgaW50cm9uX3N5biAlPiUKIyAgIG11dGF0ZShhbHQgPSBjYXNlX3doZW4oCiMgICAgIHJlZiA9PSAiRyIgfiAiQSIsCiMgICAgIHJlZiA9PSAiQSIgfiAiRyIsCiMgICAgIHJlZiA9PSAiQyIgfiAiVCIsCiMgICAgIHJlZiA9PSAiVCIgfiAiQyIKIyAgICkpICU+JQojICAgZHBseXI6OnNlbGVjdChuYW1lLCBzdGFydF9zeW4sIHJlZiwgYWx0KSAlPiUKIyAgIGRwbHlyOjpyZW5hbWUodGFyZ2V0X25hbWUgPSAibmFtZSIsIHN0YXJ0ID0gInN0YXJ0X3N5biIpICU+JQojICAgbXV0YXRlKG5hbWUgPSBwYXN0ZTAoImciLCBzdGFydCwgIl8iLCByZWYsICI+IiwgYWx0KSkgJT4lCiMgICBkcGx5cjo6c2VsZWN0KG5hbWUsIHN0YXJ0LCByZWYsIGFsdCwgdGFyZ2V0X25hbWUpICU+JQojICAgZ3JvdXBfYnkobmFtZSkgJT4lCiMgICBtdXRhdGUodGFyZ2V0X25hbWUgPSBwYXN0ZSh0YXJnZXRfbmFtZSwgY29sbGFwc2UgPSAiOyIpKSAlPiUKIyAgIGRwbHlyOjpzbGljZSgxKQoKCiMgaW50cm9uX3N5biA8LQojICAgbGVmdF9qb2luKGludHJvbl9zeW4sCiMgICAgIGR1cF9hbHRlcm5hdGl2ZXMsCiMgICAgIGJ5ID0gIm5hbWUiCiMgICApICU+JQojICAgbXV0YXRlKAojICAgICBuYW1lID0gaWZfZWxzZShpcy5uYShuYW1lX3N5bSksIG5hbWUsIG5hbWVfc3ltKSwKIyAgICAgYWx0ID0gaWZfZWxzZShpcy5uYShhbHRfMiksIGFsdCwgYWx0XzIpCiMgICApICU+JQojICAgZHBseXI6OnNlbGVjdCgtbmFtZV9zeW0sIC1hbHRfMikKCiMgc3luX3ZhcmlhbnRzIDwtCiMgICBiaW5kX3Jvd3MoCiMgICAgIGNvZGluZ19zeW4gJT4lCiMgICAgICAgbXV0YXRlKGxvY2F0aW9uID0gImNvZGluZyIpLAojICAgICBpbnRyb25fc3luICU+JQojICAgICAgIG11dGF0ZSgKIyAgICAgICAgIHBvc19wcm90ZWluID0gTkEsCiMgICAgICAgICByZWZfY29kb24gPSBOQSwKIyAgICAgICAgIHZhcl9jb2RvbiA9IE5BLAojICAgICAgICAgcmVmX2FhID0gTkEsCiMgICAgICAgICBsb2NhdGlvbiA9ICJpbnRyb24iCiMgICAgICAgKQojICAgKQojIHN5bl92YXJpYW50cwpgYGAKCgpgYGB7cn0Kc3luX3ZhcmlhbnRzIDwtCiAgbGVmdF9qb2luKAogICAgY29kaW5nX3N5biAlPiUKICAgICAgbXV0YXRlKGxvY2F0aW9uID0gImNvZGluZyIpLAogICAgdmFyaWFudHNfc252ICU+JQogICAgICBkcGx5cjo6c2VsZWN0KG5hbWUsIGlkKSwKICAgIGJ5ID0gIm5hbWUiCiAgKSAlPiUKICBtdXRhdGUoCiAgICBzeW5faW5fdGFyZ2V0ID0gbmFtZSAlaW4lIHZhcmlhbnRzX2R0JG5hbWUsCiAgICBudW1fdGFyZ2V0cyA9IHN0cl9jb3VudCh0YXJnZXRfbmFtZSwgIjsiKSArIDEKICApCnN5bl92YXJpYW50cwpgYGAKCmBgYHtyfQpzeW5fdGFyZ2V0X21hcCA8LQogIHN5bl92YXJpYW50cyAlPiUKICBncm91cF9ieShudW1fdGFyZ2V0cywgc3luX2luX3RhcmdldCkgJT4lCiAgdGFsbHkoKQpzeW5fdGFyZ2V0X21hcApgYGAKCgoKYGBge3J9CnN5bl92YXJpYW50c19sb25nIDwtCiAgc3luX3ZhcmlhbnRzICU+JQogIHNlcGFyYXRlX3Jvd3ModGFyZ2V0X25hbWUsIHNlcCA9ICI7IikgJT4lCiAgZHBseXI6OnNlbGVjdCgKICAgIHRhcmdldF9uYW1lLCBuYW1lLCBzdGFydCwgcmVmLAogICAgYWx0CiAgKSAlPiUKICBzZXRfbmFtZXMoYygKICAgICJuYW1lIiwgIm5hbWVfc3luIiwgInN0YXJ0X3N5biIsCiAgICAicmVmX3N5biIsICJhbHRfc3luIgogICkpCnN5bl92YXJpYW50c19sb25nCmBgYAoKCmBgYHtyfQp2YXJpYW50c19kdCA8LQogIGxlZnRfam9pbih2YXJpYW50c19kdCwgc3luX3ZhcmlhbnRzX2xvbmcsIGJ5ID0gIm5hbWUiKQp2YXJpYW50c19kdApgYGAKCmBgYHtyfQp2YXJpYW50c19kdCA8LQogIHZhcmlhbnRzX2R0ICU+JQogIG11dGF0ZSh0YXJnZXRfaW5fc3luID0gaWZfZWxzZShuYW1lICVpbiUgc3luX3ZhcmlhbnRzJG5hbWUsIFQsIEYpKSAlPiUKICBtdXRhdGUobmFtZV90YXJnZXRfc3luID0gcGFzdGUwKG5hbWUsICI6IiwgbmFtZV9zeW4pKSAlPiUKICBtdXRhdGUoCiAgICBjbGludmFyID0gaWZfZWxzZShncmVwbCgiXFxiXFxkK1xcYiIsIGlkKSwgVCwgRiksCiAgICBjb3NtaWMgPSBpZl9lbHNlKGdyZXBsKCJDT1NWIiwgaWQpLCBULCBGKSwKICAgIGJhc2VfZWRpdCA9IGlmX2Vsc2UoZ3JlcGwoImdcXGQrX1tBLVpdKz5bQS1aXSsiLCBpZCksIFQsIEYpCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KAogICAgbmFtZSwgaWQsIHJlZiwgYWx0LCByZWZfYWEsIHZhcl9hYSwgcG9zX3Byb3RlaW4sCiAgICB0eXBlLCBjb25zZXF1ZW5jZSwgbG9jYXRpb24sIG5hbWVfc3luLCBuYW1lX3RhcmdldF9zeW4sCiAgICB0YXJnZXRfaW5fc3luLCBhbHRfZWZmZWN0LAogICAgY2xpbnZhciwgY29zbWljLCBiYXNlX2VkaXQsIHN0YXJ0LCBzdG9wLCBzdGFydF9zeW4sIGV2ZXJ5dGhpbmcoKQogICkgICU+JQogIG11dGF0ZShuYW1lX3RhcmdldF9zeW49aWZfZWxzZShpcy5uYShuYW1lX3N5biksIE5BLCBuYW1lX3RhcmdldF9zeW4pKSAKdmFyaWFudHNfZHQKYGBgCgoKCgojIyMgU3BsaXQgdmFyaWFudHMgaW50byB0YXJnZXQsIHRhcmdldF9zeW4sIGFuZCBzeW4KCgoKYGBge3J9CnZhcmlhbnRzX3NudiA8LQogIHZhcmlhbnRzX2R0ICU+JQogIGZpbHRlcighaXMubmEobmFtZV9zeW4pKSAgCgoKdGFyZ2V0X3N5bl92YXJpYW50cyA8LQogIHZhcmlhbnRzX3NudiAgJT4lCiAgZHBseXI6OnNlbGVjdChuYW1lX3RhcmdldF9zeW4sIG5hbWVfc3luLCBuYW1lKSAlPiUKICBzZXRfbmFtZXMoYygidGFyZ2V0X25hbWUiLCJuYW1lX3N5biIsICJuYW1lX3RhcmdldCIpKSAgJT4lCiAgbXV0YXRlKCBuYW1lX3RhcmdldF9zeW49TkEsCiAgICAgICAgICBudW1fdGFyZ2V0cz1OQSwKICAgICAgICAgIHBhaXJlZF90YXJnZXRzX2FzX3N5bj0gTkEsIAogICAgICAgICAgcGFpcmVkX3RhcmdldHNfc3luX2FzX3N5bj0gTkEpICU+JQogIGRwbHlyOjpzZWxlY3QodGFyZ2V0X25hbWUsIG5hbWVfdGFyZ2V0LCBuYW1lX3N5biwgCiAgICAgICAgICAgICAgICBuYW1lX3RhcmdldF9zeW4sCiAgICAgICAgICAgICAgICBudW1fdGFyZ2V0cywgcGFpcmVkX3RhcmdldHNfYXNfc3luLAogICAgICAgICAgICAgICAgcGFpcmVkX3RhcmdldHNfc3luX2FzX3N5biApCnRhcmdldF9zeW5fdmFyaWFudHMKCmBgYAoKYGBge3J9CnN5bl92YXJpYW50c19kdCA8LQogIHZhcmlhbnRzX3NudiAlPiUKICBkcGx5cjo6c2VsZWN0KG5hbWVfc3luLCBuYW1lLCBuYW1lX3RhcmdldF9zeW4pICU+JQogIHNldF9uYW1lcyhjKCJ0YXJnZXRfbmFtZSIsICJuYW1lX3RhcmdldCIsICJuYW1lX3RhcmdldF9zeW4iICkpICAlPiUKCiAgZ3JvdXBfYnkodGFyZ2V0X25hbWUpJT4lIAogIG11dGF0ZShuYW1lX3RhcmdldCA9IHBhc3RlKG5hbWVfdGFyZ2V0LCBjb2xsYXBzZSA9ICI7IiksCiAgICAgICAgIG5hbWVfdGFyZ2V0X3N5biA9IHBhc3RlKG5hbWVfdGFyZ2V0X3N5biwgY29sbGFwc2UgPSAiOyIpKSAlPiUgCiAgZHBseXI6OnNsaWNlKDEpICAgICU+JSAKICBtdXRhdGUoIAogICAgICAgICAgc3luX2luX3RhcmdldCA9IHRhcmdldF9uYW1lICVpbiUgdmFyaWFudHNfZHQkbmFtZSwKICAgICAgICAgIG51bV90YXJnZXRzID0gc3RyX2NvdW50KG5hbWVfdGFyZ2V0LCAiOyIpICsgMQogICkKCgpzeW5fdmFyaWFudHNfZHRfZmlsdGVyZWQgPC0KICBzeW5fdmFyaWFudHNfZHQgJT4lCiAgbXV0YXRlKHRhcmdldF9pbl9zeW49TkEsCiAgICAgICAgbmFtZV9zeW49TkEsCiAgICAgICAgcGFpcmVkX3RhcmdldHNfYXNfc3luPSBOQSwgCiAgICAgICAgcGFpcmVkX3RhcmdldHNfc3luX2FzX3N5bj0gTkEpICU+JQogIGZpbHRlcighKHN5bl9pbl90YXJnZXQpKSU+JQogIGRwbHlyOjpzZWxlY3QodGFyZ2V0X25hbWUsIG5hbWVfdGFyZ2V0LCBuYW1lX3N5biwKICAgICAgICAgICAgICAgIG5hbWVfdGFyZ2V0X3N5biwKICAgICAgICAgICAgICAgIG51bV90YXJnZXRzLCAKICAgICAgICAgICAgICAgIHBhaXJlZF90YXJnZXRzX2FzX3N5biwgCiAgICAgICAgICAgICAgICBwYWlyZWRfdGFyZ2V0c19zeW5fYXNfc3luICkKCnN5bl92YXJpYW50c19pbl90YXJnZXQgPC0KICAgIHN5bl92YXJpYW50c19kdCAlPiUKICBmaWx0ZXIoKHN5bl9pbl90YXJnZXQpKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1zeW5faW5fdGFyZ2V0KSAlPiUKICByZW5hbWUobmFtZV90YXJnZXQ9InBhaXJlZF90YXJnZXRzX2FzX3N5biIsCiAgICAgICAgIG5hbWVfdGFyZ2V0X3N5bj0icGFpcmVkX3RhcmdldHNfc3luX2FzX3N5biIpCnN5bl92YXJpYW50c19kdF9maWx0ZXJlZApgYGAKCgoKYGBge3J9CnZhcmlhbnRzX2R0IDwtCiAgbGVmdF9qb2luKHZhcmlhbnRzX2R0LCAKICAgICAgICAgIHN5bl92YXJpYW50c19pbl90YXJnZXQgJT4lCiAgcmVuYW1lKHRhcmdldF9uYW1lPSJuYW1lIiksCiAgYnk9Im5hbWUiKQp2YXJpYW50c19kdApgYGAKCmBgYHtyfQp0YXJnZXRfdmFyaWFudHMgPC0KICB2YXJpYW50c19kdCAlPiUKICBkcGx5cjo6c2VsZWN0KG5hbWUsIG5hbWVfc3luLCBuYW1lX3RhcmdldF9zeW4sIHBhaXJlZF90YXJnZXRzX2FzX3N5biwgcGFpcmVkX3RhcmdldHNfc3luX2FzX3N5biwgbnVtX3RhcmdldHMpICU+JQogIHNldF9uYW1lcyhjKCJ0YXJnZXRfbmFtZSIsICJuYW1lX3N5biIsICJuYW1lX3RhcmdldF9zeW4iLCAicGFpcmVkX3RhcmdldHNfYXNfc3luIiwgInBhaXJlZF90YXJnZXRzX3N5bl9hc19zeW4iLCAibnVtX3RhcmdldHMiKSkgICU+JQogIG11dGF0ZShuYW1lX3RhcmdldD1OQSkgICU+JQogIGRwbHlyOjpzZWxlY3QodGFyZ2V0X25hbWUsIG5hbWVfdGFyZ2V0LCBuYW1lX3N5biwKICAgICAgICAgICAgICAgIG5hbWVfdGFyZ2V0X3N5biwKICAgICAgICAgICAgICAgIG51bV90YXJnZXRzLCAKICAgICAgICAgICAgICAgIHBhaXJlZF90YXJnZXRzX2FzX3N5biwgCiAgICAgICAgICAgICAgICBwYWlyZWRfdGFyZ2V0c19zeW5fYXNfc3luICkKICAKdGFyZ2V0X3ZhcmlhbnRzCmBgYAoKCmBgYHtyfQphbGxfdmFyaWFudHNfcGFpcmVkIDwtIAogIGJpbmRfcm93cyh0YXJnZXRfdmFyaWFudHMsIAogICAgICAgICAgICB0YXJnZXRfc3luX3ZhcmlhbnRzLCAKICAgICAgICAgICAgc3luX3ZhcmlhbnRzX2R0X2ZpbHRlcmVkKQphbGxfdmFyaWFudHNfcGFpcmVkIApgYGAKCgoKIyMjIFNhdmUgdG8gZmlsZXMKCmBgYHtyfQp3cml0ZV90c3YoCiAgYWxsX3ZhcmlhbnRzX3BhaXJlZCwKICBhbGxfdmFyaWFudHNfcGFpcmVkX3BhdGgKKQoKd3JpdGVfdHN2KAogIHZhcmlhbnRzX2R0LCB2YXJpYW50c190YWJsZV9wYXRoKQpgYGAKCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK